Backbone: Multiple views subscribing to a single collection's event? Is this a bad practice?

backbone.js, design-patterns, javascript, javascript-framework

Solution

What you're doing is perfectly fine and part of the reason for using Backbone. From the Backbone introduction:

Whenever a UI action causes an attribute of a model to change, the model triggers a "change" event; all the Views that display the model's state can be notified of the change,

Note that they say "all the Views", not "the view".

One example of multiple views for a single collection would be a chat system. Suppose you have a collection of users that are online; then you might have one simple view in the header that displays the number of people that are online and another view (of the same collection) that lists the users:

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

Then you'd have one `OnlineUsers` instance but both your `CounterView` and `ListView` instances would be watching it. When people come online or go offline, both views will be updated as desired.

Simple demo: http://jsfiddle.net/ambiguous/eX7gZ/

The above example situation sounds like exactly the sort of thing you're doing and it is exactly the sort of thing that Backbone is for. Good job.

Problem

I have a question, really basic stuff I think but: I've only seen examples with a collection view and single view dependent on a collection being updated. What if you have multiple views trying to subscribe to a collections events, ie reset, addOne, addAll etc... Am I missing some point about doing/not doing this? Do you have any examples of this? Does that even make sense? Any info is MUCH appreciated ``` 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}); ```

Original source