Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging Backbone.js: rendering after collection fetch()

Tags:

backbone.js

I'm trying to do basic render() after fetch() on collection (Backbone 0.9.2):

var ProjectListView = Backbone.View.extend({
        el: $('#container'),
        initialize: function () {
            this.collection = new ProjectsCollection();
            this.collection.bind("change", _.bind(this.render, this));
            this.collection.fetch({ success: function () { console.log("collection fetched"); } });
            ...
             }, 
        render: function () {
            console.log("rendered");
            ...

Creating new View instance prints out:

    collection fetched 

So the render() never gets called after fetch(). What am I doing wrong here? There are no exceptions present.

Any tips how to debug these sort of things in backbone?

Ps. It seems that this feature is poorly documented given the number of questions on SO.

like image 994
Fdr Avatar asked Nov 27 '12 06:11

Fdr


2 Answers

From the fine manual:

fetch collection.fetch([options])

Fetch the default set of models for this collection from the server, resetting the collection when they arrive. [...] When the model data returns from the server, the collection will reset.

And what does reset do? reset does this:

reset collection.reset(models, [options])

[...] Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single "reset" event at the end.

So fetch calls reset to update the collection's models and reset triggers a "reset" event, not a "change" event. None of the models have changed and a collection's "change" events come from its models.

You should have render bound to "reset":

initialize: function () {
    this.collection = new ProjectsCollection();
    this.collection.bind("reset", _.bind(this.render, this));
    this.collection.fetch(...);
}

If you want to listen for "change" events on the contained models then you can bind a "change" handler to the collection since:

You can bind "change" events to be notified when any model in the collection has been modified,
[...]
Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience.

The collection will also generate "add" and "remove" events as the collection itself changes.


Newer versions of Backbone no longer reset collections during fetch:

When the model data returns from the server, it uses set to (intelligently) merge the fetched models, unless you pass {reset: true}, in which case the collection will be (efficiently) reset.

And set:

[...] performs a "smart" update of the collection with the passed list of models. If a model in the list isn't yet in the collection it will be added; if the model is already in the collection its attributes will be merged; and if the collection contains any models that aren't present in the list, they'll be removed. All of the appropriate "add", "remove", and "change" events are fired as this happens

So with newer versions of Backbone you'll want to list for the "add", "remove", and "change" events (which a collection based view should be listening for anyway); you could also use {reset: true} on the initial fetch and listen to "reset" as well. I'd recommend the following approach for collection based views:

  1. Listen to "add" and handle that event with a callback that simply adds one item to the view, don't throw everything away and re-render.
  2. Listen to "remvoe" and handle that event with a callback that only removes the newly removed model.
  3. Listen to "change" and handle that with a callback that replaces (or updates) the appropriate item.
  4. Listen to "reset" and bind that to render. Then pass {reset: true} to the collection's initial fetch call.

That will trap the important events and the collection-view will do the minimal amount of work to handle each one. Of course, this strategy isn't applicable to all situations but I think it is a good starting point.

like image 91
mu is too short Avatar answered Sep 20 '22 06:09

mu is too short


This changed in 1.0

http://backbonejs.org/#changelog

"If you'd like to continue using "reset", pass {reset: true}."

like image 21
Dane Macaulay Avatar answered Sep 18 '22 06:09

Dane Macaulay