Search code examples
javascriptbackbone.jsbackbone.js-collections

Best method for rendering collection in Backbone


I'm using Backbone and I have the following model and collection

App.Models.Person = Backbone.Model.extend({
});

App.Collections.People = Backbone.Collection.extend({
    model: App.Models.Person,
    url: 'api/people',
});

However what I'm struggling on is the best way to render this collection. Here's how I've done it so far which works but doesn't seem to be the most elegant solution

App.Views.PeopleView = Backbone.View.extend({
    el: $(".people"),

    initialize: function () {
        this.collection = new App.Collections.People();

        //This seems like a bad way to do it?
        var that = this;
        this.collection.fetch({success: function(){
            that.render();
        }});
    },

    render: function () {
        var that = this;
        _.each(this.collection.models, function (item) {
            that.renderPerson(item);
        }, this);
    },

I'm fairly new to Backbone but have to assign this to a different variable to I use it inside of the success function just seems like a bad way of doing things? Any help on best practices would be appreciated.


Solution

  • Backbone allows you to register for events that you can react to. When the collection is synchronized with the server, it will always fire the sync event. You can choose to listen for that event and call any given method. For instance ...

    initialize: function () {
        this.collection = new App.Collections.People();
        this.listenTo(this.collection, "sync", this.render);
    
        // Fetch the initial state of the collection
        this.collection.fetch();
    }
    

    ... will set up your collection so that it would always call this.render() whenever sync occurs.

    The docs on Backbone Events are succinct but pretty good. Keep in mind a few things:

    • The method you use to register event listeners (i.e. listenTo or on) changes how you provide the context of the called function. listenTo, for instance, will use the current context automatically; on will not. This piece of the docs explains it pretty well.
    • If you need to remove a view, you will need to disconnect event listeners. The easiest way to do that is to use listenTo to connect them in the first place; then when destroying the view you can just call view.stopListening().

    For rendering, there are a lots of suggestions for how to do it. Generally having a view to render each individual model is one way. You can also use Backbone.Collection#each to iterate over the models and control the scope of the iterating function. For instance:

    render: function() {
        this.collection.each(function(model) {
            var view = new App.Collections.PersonView({ model: model });
            view.render();
            this.$el.append(view.$el);
        }, this);    
    }
    

    Note the second argument to .each specifies the scope of the iterator. (Again, have a look at the docs on controlling scope. If you'd rather have a framework help out with the rendering, check out Marionette's CollectionView and ItemView.