Search code examples
backbone.jssocket.io

backbone.js: Update model, re-sort and re-render models collection


In my app I have a socket.io connection that is listening to the backend and getting updates to models held by the clients browser (which retrieves the model by id and calls set on the model attribute).

I'd like the collection to be sorted, then re-rendered as a whole in order to reflect any new ordering on the models as a result of the set (most examples seem to be around individual views being re-rendered). What's a method of achieving this?

NB I've got a backbone.js layout lifted pretty verbatim from the example todo app (this is the first backbone app).


Solution

  • You can achieve what you want by providing a comparator method for your collection.

    Example:

    ModelCollection = Backbone.Collection.extend({
        comparator: function(a, b) {
            if ( a.get("name") > b.get("name") ) return 1;
            if ( a.get("name") < b.get("name") ) return -1;
            if ( a.get("name") === b.get("name") ) return 0;
        },
    
        initialize: function() {
            this.on('change:name', function() { this.sort() }, this);
        }
    });
    

    The comparator in this example will cause your collection to be sorted in ascending order by the name attribute of the models inside.

    Note that your collection won't be sorted automatically when changing attribute(s) of any of its models. By default, sorting happens only when creating new models and adding them to the collection; but the comparator will be used by the collection.sort method.

    The code above takes advantage of this by setting an event listener that simply re-sorts the collection on any changes to the name attributes of its models.

    To complete the picture, we set up an appropriate event listener in the View associated with the collection to make sure it re-renders on any changes:

    CollectionView = Backbone.View.extend({
        initialize: function() {
            this.collection = new ModelCollection();
            this.collection.on('all', function() { this.render() }, this);
        },
    
        render: function() {
            this.$el.html(this.collection.toJSON());
        }
    });
    

    That's it :)


    Relevant excerpt from the Backbone documentation:

    By default there is no comparator for a collection. If you define a comparator, it will be used to maintain the collection in sorted order. This means that as models are added, they are inserted at the correct index in collection.models. A comparator can be defined as a sortBy (pass a function that takes a single argument), as a sort (pass a comparator function that expects two arguments), or as a string indicating the attribute to sort by. [...] Collections with a comparator will not automatically re-sort if you later change model attributes, so you may wish to call sort after changing model attributes that would affect the order.