Search code examples
backbone.js

Backbone pre rendering of collection models


I want to perform an action, clearing parent element, after a collection has fetched his models but prior to the models rendering.

I've stumbled upon before and after render methods yet they are model specific, which will cause my parent element to clear before every model rendering.

I'm able of course to perform the action pre-fetching yet I want it to occur when fetch is done and before models are rendered.

I tried using reset and change events listening on the collection yet both resulted unwanted end result. Reset event seamed to go in that direction yet the passed argument was the entire collection and not a single model from the collection, therefore using the add event callback wasn't possible due to difference in argument type (collection and not a model as required)

Any ideas how to invoke a callback when fetch a collection fetch is successful yet models are yet to be rendered?

The model contains the returned attributes while collection contains url for fetching and parse method to return argument wrapped object.

Below is the code I use to render the collection view, which is basically rendering each model's view within the collection.

Collection View
---------------
var FoosView = Backbone.View.extend({
        el: '#plans',
        events: {
            //'click tr': 'rowClick'
        },
        initialize: function() {
            this.listenTo(this.collection, 'add', this.renderNew);

            _.bindAll(this, "render");

            this.render();
        },
        renderNew: function(FooModel) {

            var item = new FooView({model: FooModel});
            this.$el.prepend(item.render().$el);
        }
        ...
    });

The model view
--------
var FooView = Backbone.View.extend({
        tagName: 'li',
        initialize: function(options) {
            this.options = options || {};
            this.tpl = _.template(fooTpl);
        },
        render: function() {
            var data = this.model.toJSON();

            this.$el.html(this.tpl(data));

            return this;
        }
    });

Thanks in advance.


Solution

  • OK, I think I understand your question and here is a proposed solution. You are now listening to the reset event on your collection and calling this.renderAll. this.renderAll will take the list of models from the collection and render them to the page, but only AFTER the list element has been emptied. Hope this helps :).

    var FoosView = Backbone.View.extend({
        el: '#plans',
        collection: yourCollection, // A reference to the collection.
        initialize: function() {
            this.listenTo(this.collection, 'add', this.renderNew);
            this.listenTo(this.collection, 'reset', this.renderAll);
        },
        renderAll: function() {
            // Empty your list.
            this.$el.empty(); 
            var _views = []; // Create a list for you subviews
            // Create your subviews with the models in this.collection.
            this.collection.each(function(model) {
                _views.push(new FooView({model: model});
            });
            // Now create a document fragment so as to not reflow the page for every subview.
            var container = document.createDocumentFragment();
            // Append your subviews to the container.
            _.each(_views, function(subview) {
                container.appendChild(subview.render().el);
            });
            // Append the container to your list.
            this.$el.append(container);
        },
        // renderNew will only run on the collections 'add' event.
        renderNew: function(FooModel) {
            var item = new FooView({model: FooModel});
            this.$el.prepend(item.render().$el);
        }
    });
    

    I am forced to assume a few things about you html, but I think the above code should be enough to get you up and running. Let me know if it works.