Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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});
like image 932
daedelus_j Avatar asked Apr 26 '12 20:04

daedelus_j


People also ask

What is collection in backbonejs?

BackboneJS - Collection. Collections are ordered sets of Models. We just need to extend the backbone's collection class to create our own collection. Any event that is triggered on a model in a collection will also be triggered on the collection directly. This allows you to listen for changes to specific attributes in any model in a collection.

What are backbone views?

Backbone views can be thought of as: Observers that keep listening to the DOM events and in case the event fires, taking the appropriate actions. Objects backed by models that are responsible for rendering the model data on the screen. Let us see how we can use backbone.js views to efficiently manage the applications.

What is the use of events in backbone?

The Backbone object itself mixes in Events, and can be used to emit any global events that your application needs. "add" (model, collection, options) — when a model is added to a collection. "remove" (model, collection, options) — when a model is removed from a collection.

How to listen for changes to specific attributes in a collection?

We just need to extend the backbone's collection class to create our own collection. Any event that is triggered on a model in a collection will also be triggered on the collection directly. This allows you to listen for changes to specific attributes in any model in a collection.


2 Answers

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.

like image 150
mu is too short Avatar answered Oct 23 '22 05:10

mu is too short


We had the same problem. We needed to use multiple jQuery Templates with unique internal events for each model without using multiple holder views. This is the solution that we came up with:

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

Full source and working example: http://jsfiddle.net/HLv5z/9/

like image 24
Erik Avatar answered Oct 23 '22 06:10

Erik