关于javascript:如何在”列表”和”项目”视图中呈现”Backbone “集合?

How do I render a Backbone Collection in a List and Item View?

我正在使用一个可在html列表中呈现用户所有联系人的联系人栏。

我所拥有的:

  • UserModel-这是带有用户名和电子邮件的简单Backbone.Model
  • UserCollection-用作联系人列表
  • ContactsView-这是ul联系人列表
  • ContactView-这是一个呈现为li的单一联系人模型
  • 我目前正在就如何(在何处)如何获取UserCollection以及如何将单个模型传递给单个ContactView项的解决方案大惊小怪。

    具体障碍为:

  • 我应该在哪里获取,存储UserCollection
  • 如何呈现联系人列表
  • 如何呈现联系方式
  • 如何防止fetch({success:callback})破坏我的代码结构
  • 我当前的代码是这样的:

    进入点:

    1
    2
    3
    4
    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 });

    联系人视图:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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;

    });

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

    欢迎使用示例链接。不幸的是,我将代码风格定位在待办事项列表上,但是待办事项列表还不是那么高级...

    更新的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    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?


    首先:为什么要获取视图?Backbone 视图没有获取方法。.

    1提取UserCollection的正确位置将在视图的initialize方法内部:

    1
    2
    3
    4
    5
    6
    7
    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函数中做到这一点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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方法中进行无用的调用,也不会使代码混乱。在这里也请绑定全部。

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

    我不知道您在这里要问什么,但我认为最好的方法是不使用成功回调。只要对集合和模型进行了处理,它们就会触发事件,因此,使用它们比成功回调更健壮和可靠。查看事件目录以了解更多信息。 Christophe Coenraets的《酒窖》教程是这种listview-listitemview安排的一个很好的例子。

    希望这会有所帮助!

    更新:添加了_.bindAlls以解决事件绑定渲染调用中的问题。有关绑定此对象的一些信息。


    注意:所有代码均已简化且未经测试

    当我定义了所有元素结构并实现了所有模型,集合和视图时,我将实现一个加载器,该加载器负责触发获取和呈现操作。

    首先,我需要从外部公开类定义,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 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...

    然后实现所谓的加载程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 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();
    });

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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文件:

  • App.js
  • 您的模型,集合,视图
  • AppLoad.js
  • 使用此系统,您可以获得:

    • 外部访问您的收藏集,以防您需要从其他地方访问它。
    • CollectionView.el的外部控制具有更好的去耦和测试能力。
    • CollectionView将自动响应Collection中的更改

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