如何在列表和项目视图中呈现Backbone集合?

时间:2012-07-25 09:13:08

标签: javascript backbone.js

我正在制作一个联系栏,用于在html列表中呈现用户的所有联系人。

我有什么:

  1. UserModel - 这是一个简单的Backbone.Model,包含用户名和电子邮件
  2. UserCollection - 用作联系人列表
  3. ContactsView - 这是ul联系人列表
  4. ContactView - 这是一个以li
  5. 呈现的单一联系人模型

    我目前正在解决一个解决方案,我可以获取我的UserCollection以及如何将单个模型传递给单个ContactView项目。

    具体障碍是:

    1. 我应该在哪里获取,存储UserCollection
    2. 如何呈现联系人列表
    3. 如何呈现联系人项目
    4. 如何阻止fetch({success:callback})破坏我的代码结构
    5. 我目前的代码是:

      入口点:

      // create a new instance of the contact list view
      var view = new ContactsView();
      // insert the rendered element of the contact list view in to the dom
      $('div.contacts-body').html(view.render().el);
      view.fetch({ success: view.loadContacts });
      

      ContactsView:

      define(
          ['jquery', 'underscore', 'backbone', 'text!templates/conversations/contacts.html', 'collections/users', 'views/conversations/contact'],
          function($, _, Backbone, ContactsTemplate, UserCollection, ContactView) {
      
              var ContactsView = Backbone.View.extend({
      
                  tagName: "ul",
      
                  className: "contacts unstyled",
      
                  attributes: "",
      
                  // I am feeling uneasy hardcoding the collection into the view
                  initialize: function() {
                      this.collection = new UserCollection();
                  },
      
                  // this renders our contact list
                  // we don't need any template because we just have <ul class="contacts"></ul>
                  render: function() {
                      this.$el.html();
                      return this;
                  },
      
                  // this should render the contact list
                  // really crappy and unflexible
                  loadContacts: function() {
                      this.collection.each(function(contact) {
                          // create a new contact item, insert the model
                          var view = new ContactView({ model: contact });
                          // append it to our list
                          this.$el.append(view.render().el);
                      });
                  }
      
              });
      
              return ContactsView;
      
      });
      

      ContactView

      define(
          ['jquery', 'underscore', 'backbone', 'text!templates/conversations/contact.html'],
          function($, _, Backbone, ContactTemplate) {
      
              var ContactView = Backbone.View.extend({
      
                  tagName: "li",
      
                  className: "contact",
      
                  attributes: "",
      
                  template:_.template(ContactTemplate),
      
                  initialize: function() {
                      this.model.bind('change', this.render, this);
                      this.model.bind('destroy', this.remove, this);
                  },
      
                  render: function() {
                      this.$el.html(this.template(this.model.toJSON()));
                      return this;
                  }
      
              });
      
              return ContactView;
      
      });
      

      有人可以帮助我解决我的四个障碍。

      欢迎良好的示例链接。我在todos列表中定位了我的代码风格,不幸的是todos列表不是那么先进......

      更新后的代码:

      define(
          ['jquery', 'underscore', 'backbone', 'text!templates/conversations/contacts.html', 'collections/users', 'views/conversations/contact'],
          function($, _, Backbone, ContactsTemplate, UserCollection, ContactView) {
      
              var ContactsView = Backbone.View.extend({
      
                  tagName: "ul",
      
                  className: "contacts unstyled",
      
                  attributes: "",
      
                  events: {
      
                  },
      
                  initialize: function() {
                      this.collection = new UserCollection();
                      this.collection.on('reset', this.render);
                      this.collection.fetch();
                  },
      
                  render: function() {
                      // in chromium console
                      console.log(this.el); // first: html, second: undefined
                      console.log(this.$el); // first: html in array, second: undefined
                      this.$el.empty(); // error on the called that this.$el is undefined
      
                      this.collection.each(function(contact) {
                          var view = new ContactView({ model: contact });
                          this.$el.append(view.el);
                      }.bind(this));
      
                      return this;
                  }
      
              });
      
              return ContactsView;
      

      重置是否会触发this.render两次?

2 个答案:

答案 0 :(得分:4)

首先:你为什么要获取视图?骨干视图没有fetch方法..

1获取UserCollection的正确位置将在视图的initialize方法中:

initialize: function() { // ContactsView
  _.bindAll(this, 'render', 'otherMethodName', ...); // Bind this to all view functions
  ...
  this.collection.on('reset', this.render); // bind the collection reset event to render this view
  this.collection.fetch();
  ...
}

现在,您可以在需要时准确地获取联系人。下一步是渲染集合。

2绑定到reset事件会使loadContacts方法过时,我们可以在render函数中执行此操作:

render: function() {
  this.$el.empty(); // clear the element to make sure you don't double your contact view
  var self = this; // so you can use this inside the each function

  this.collection.each(function(contact) { // iterate through the collection
    var contactView = new ContactView({model: contact}); 
    self.$el.append(contactView.el);
  });

  return this;
}

现在,您可以在render方法中呈现您的联系人列表,并在其中完成。

3 ContactView实际上看起来不错。

只需使项目在initialize方法中呈现自己,这样就不必在ContactsView的render方法中进行无用的调用,并使代码混乱。也在这里绑定所有。

initialize: function() { // ContactView
  _.bindAll(this, 'render', 'otherMethodName', ...);
  ...
  this.render(); // Render in the end of initialize
}

我不知道你在这里问的是什么,但我认为最好的办法就是不要使用成功的回调。集合和模型会在事件发生时触发事件,因此点击它们比成功回调更加强大和可靠。 Check out the catalog of events to learn more. Christophe Coenraets的Wine Cellar tutorial就是这种listview-listitemview安排的一个很好的例子。

希望这有帮助!

更新:添加_.bindAlls以在事件绑定渲染调用中修复此问题。 Some info on binding this.

答案 1 :(得分:2)

注意:所有代码都已简化,未经过测试

如果我已经定义了所有的元素结构,并且实现了所有的模型,集合和视图,那么我实现了一个 Loader ,它负责触发获取和渲染操作。< / p>

首先,我需要从外部公开类定义:

// App.js
var App = {}

// ContactsCollection.js
$(function(){
  var App.ContactsCollection = Backbone.Collection.extend({ ... });
});

// ContactsView.js
$(function(){
  var App.ContactsView = Backbone.View.extend({ ... });
});

// and so on...

然后我实现了我所谓的 Loader

// AppLoad.js
$(function(){

  // instantiate the collection
  var App.contactsCollection = new App.ContactsCollection();

  // instantiate the CollectionView and assign the collection to it
  var App.contactsView = new App.ContactsView({
    el: "div.contacts-body ul",
    collection: App.contactsCollection
  });

  // fetch the collection the contactsView will
  // render the content authomatically
  App.contactsCollection.fetch();
});

您需要做的另一项更改是以响应ContactsView中的更改的方式配置App.contactsCollection,因为fetch()是异步的,您可以调用render()当集合仍未加载时,您必须告诉CollectionView在集合准备就绪时将其呈现为自我:

var ContactsView = Backbone.View.extend({
  initialize: function( opts ){
    this.collection.on( 'reset', this.addAll, this );
    this.collection.on( 'add', this.addOne, this );
    // ... same with 'remove'
  },

  addOne: function( model ){
    var view = new App.ContactView({ model: contact });
    this.$el.append( view.render().el );
  },

  addAll: function(){
    this.collection.each( $.proxy( this.addOne, this ) );
  }
});

您必须以正确的顺序要求您的js文件:

  1. App.js
  2. 您的模特,收藏品,观点
  3. AppLoad.js
  4. 使用此系统,您将获得:

    • 外部访问您的收藏集,以防您需要从其他地方访问它。
    • CollectionView.el的外部控制更适合解耦和测试。
    • CollectionView将自动响应集合中的更改

    注意:如果您使用Router,则可以将AppLoad.js逻辑移至那里。