Backbone:订阅单个集合事件的多个视图?这是一个不好的做法吗?

Posted

技术标签:

【中文标题】Backbone:订阅单个集合事件的多个视图?这是一个不好的做法吗?【英文标题】:Backbone: Multiple views subscribing to a single collection's event? Is this a bad practice? 【发布时间】:2012-05-07 15:23:47 【问题描述】:

我有一个问题,我认为是非常基本的问题,但是:

我只看到过带有集合视图和依赖于正在更新的集合的单个视图的示例。如果您有多个视图尝试订阅集合事件,例如 reset、addOne、addAll 等,该怎么办?

我是否错过了做/不做这件事的一些要点?你有这方面的例子吗?这有意义吗?

任何信息都非常感谢

    var Coll = Backbone.Collection.extend(
        model: SingleModel,
        url: 'service',
        initialize: function()
            console.log('collection inited')

                

    );

    var SingleModel = Backbone.Collection.extend();            

    var CollView = Backbone.View.extend(                
        el: 'ul',
        template: Handlebars.compile(someContainerTemplate),
        init: function()
            _.bindAll(this, 'render', 'addAll', 'addOne');
            this.collection.bind("reset", this.addAll);

            this.collection.fetch();
        ,
        render: function()
            $(this.el).append(this.template())
        ,

        addAll: function()
            this.collection.each(this.addOne);
        ,

        addOne: function(model)
            var view = new SingleView( model: model )            
        
    )

    var SingleView = Backbone.View.extend(
        tagName: "li",
        events: 
            "click .delete": "remove"                    
        ,
        template: Handlebars.compile(someTemplateForSingleItem),  
        initialize: function() 
        _.bindAll(this,'render');

            this.model.bind('save', this.addOne);
            this.model.bind('destroy', removeEl);
        ,

        remove: function()
          this.model.destroy();  
        ,

        removeEl: function()
          $(this.el).remove();  
        ,

        render: function() 
            var context = this.model.toJSON();
            return $(this.el).append(this.template(context));
        ,       
    )


    // standard so far (excluding any bad practices), 
    // but what if you have another view dependent on 
    // say the number of length of the collection, and 
    // you want it to update if any single models are destroyed

    var HeaderView = Backbone.View.extend(
        tagName: "div#header",
        template: Handlebars.compile(someHeaderTemplate), 
        initialize: function() 
        _.bindAll(this,'render');

        this.model.bind('save', this.addOne);
        ,

        render: function() 
            //assigning this collection length

            var context = this.collection.length;
            return $(this.el).append(this.template(context));
        ,      
    );        

    var coll = new Coll();
    new CollView( collection: coll );
    new HeaderView( collection: coll);

【问题讨论】:

【参考方案1】:

您所做的一切都很好,这也是使用 Backbone 的部分原因。来自Backbone introduction:

每当 UI 动作导致模型的某个属性发生变化时,模型都会触发 "change" 事件;所有显示模型状态的 View 都可以收到更改通知,

请注意,他们说的是“所有视图”,而不是“视图”。

一个集合的多个视图的一个例子是聊天系统。假设您有一组在线用户;那么您可能在标题中有一个显示在线人数的简单视图和另一个列出用户的视图(同一个集合):

var User = Backbone.Model.extend();
var OnlineUsers = Backbone.Collection.extend(
    model: User
);

var CounterView = Backbone.View.extend(
    tagName: 'span',
    initialize: function() 
        _.bindAll(this, 'render');
        this.collection.on('add', this.render);
        // other interesting events...
    ,
    render: function() 
        this.$el.text(this.collection.size());
        return this;
    
);
var ListView = Backbone.View.extend(
    initialize: function() 
        _.bindAll(this, 'render');
        this.collection.on('add', this.render);
        // other interesting events...
    ,
    render: function() 
        var html = this.collection.map(function(m) 
            return '<p>' + m.get('name') + '</p>';
        );
        this.$el.html(html.join(''));
        return this;
    
);

那么您将拥有一个 OnlineUsers 实例,但您的 CounterViewListView 实例都在观看它。当人们上线或下线时,两个视图都将根据需要更新。

简单演示:http://jsfiddle.net/ambiguous/eX7gZ/

上面的示例情况听起来正是您正在做的事情,而这正是 Backbone 所要做的事情。干得好。

【讨论】:

感谢您的回答,有道理!【参考方案2】:

我们遇到了同样的问题。我们需要为每个模型使用具有唯一内部事件的多个 jQuery 模板,而不使用多个持有者视图。这是我们提出的解决方案:

var myHolderView = Backbone.View.extend(
    el: '#views',
    render: function()
        // This is because 'this' change inside the collection.each
        var $this = this;

        // If you want a wrapper template
        var wrapperHtml = $('#view-wrapper-template').tmpl();
        $this.$el.append(wrapperHtml);
        $wrapper = $this.$el.find('> div'); // If wrapper is a div


        $this.collection.each(function(model)
            // Render and append the viewA with internal events
            var viewA = new myViewA(model: model);
            viewA.render();

            // Use this to add the views content (viewA.el) to this views element ('#views')
            //$this.$el.append(viewA.el);

            // Use this to add the view content (viewA.el) to this views wrapper element ($wrapper)
            $wrapper.append(viewA.el);


            // Render and append the viewB with internal events
            var viewB = new myViewB(model: model);
            viewB.render();
            //$this.$el.append(viewB.el);
            $wrapper.append(viewB.el);


            // Add more views here...
        );
    
);

完整的源代码和工作示例: http://jsfiddle.net/HLv5z/9/

【讨论】:

以上是关于Backbone:订阅单个集合事件的多个视图?这是一个不好的做法吗?的主要内容,如果未能解决你的问题,请参考以下文章

Backbone Marionette慢速复合视图(200多个系列)

Backbone:同一模型的多个视图模型

Backbone.js:组合多个模型的复杂视图

Backbone.js:清除所有视图的最佳方法

如何清除Backbone僵尸视图

模型和集合到 Backbone 中的一个视图