9

我正在开发一个联系人栏,它在 html 列表中呈现用户的所有联系人。

我有的:

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

我目前正在对如何(以及在​​何处)获取我的 UserCollection 以及如何将单个模型传递给单个 ContactView 项目的解决方案感到头疼。

具体障碍是:

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

我目前的代码是这样的:

入口点:

// 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 });

联系人查看:

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;

});

联系人查看

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;

});

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

欢迎使用良好的示例链接。我将我的代码风格定位在待办事项列表中,不幸的是,待办事项列表并不是那么先进......

更新代码:

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;

会不会是 reset 触发了 this.render 两次?

4

2 回答 2

4

首先:为什么要获取视图?主干视图没有 fetch 方法。

1 获取 UserCollection 的正确位置是在视图的初始化方法中:

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 绑定到重置事件会使您的 loadContacts 方法过时,我们可以在渲染函数中执行此操作:

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 的渲染方法中进行无用的调用并弄乱您的代码。也在这里 bindAll 。

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

我不知道你在这里问什么,但我认为最好的方法是不使用成功回调。每当对它们执行某些操作时,集合和模型都会触发事件,因此利用它们比成功回调更健壮和可靠。查看活动目录以了解更多信息。Christophe Coenraets的Wine Cellar 教程是这种 listview-listitemview 安排的一个很好的例子。

希望这可以帮助!

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

于 2012-07-25T10:02:53.330 回答
2

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

当我像你一样定义了所有元素结构时,实现了所有模型、集合和视图,然后我实现了一个加载器,它负责触发获取和渲染操作。

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

// 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. 应用程序.js
  2. 您的模型、集合、视图
  3. 应用加载.js

使用该系统,您可以获得:

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

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

于 2012-07-25T10:17:35.073 回答