Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone: How to update a collection view when collection changes?

I'm relatively new to Backbone.js. I'm initializing a collection view and passing in a collection at creation time.

suggestionsView = new TreeCategoriesAutoSuggest.Views.Suggestions({
    collection: new App.Collections.Suggestions(this.getSuggestions(query))
});

I then render the collection view. Each time a user enters a query into a text box the collection is regenerated and assigned to the collection view using:

suggestionsView.collection.set(this.getSuggestions(query));

This takes care of the adding/removing of models in the collection but how do I manage the adding/removing of views for the added/removed models?

I should mention that I have used the this.collection.on("add") listener in the collection view. But this gets triggered for every model that is added. I also tried this.model.on("change") from within the individual view but this is not fired when models are added/removed from collections.

Any help/guidance appreciated!

Update

I am now using:

suggestionsView.collection.reset(this.getSuggestions(query));

And when the reset event is fired I'm removing the suggestion sub views, re-initializing them for the new collection and re-rendering the collection view.

handleReset: function(){
    console.log("reset");
    this.cleanupOldViews();
    this.initViews();
},

initViews: function(){
    this.collection.each(function(suggestion){
        this.suggestionViews.push(new TreeCategoriesAutoSuggest.Views.Suggestion({
            model: suggestion
        }));
    },this);        
},

cleanupOldViews: function(){
    _.each(this.suggestionViews,function(suggestionView){
        suggestionView.remove()
    },this);

    this.suggestionViews = [];
}

So you think I don't need to worry about destroying the models?

like image 333
Alex B Avatar asked Dec 10 '13 22:12

Alex B


1 Answers

Bulk replacements are easier and more efficient using reset:

reset collection.reset([models], [options])

Adding and removing models one at a time is all well and good, but sometimes you have so many models to change that you'd rather just update the collection in bulk. Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single "reset" event at the end. Returns the newly-set models. For convenience, within a "reset" event, the list of any previous models is available as options.previousModels.

So instead of using set to merge the changes and generate a bunch of 'add' and 'remove' events, using reset and listen for 'reset' event:

// In the view's `initialize`...
this.listenTo(this.collection, 'reset', this.render);

and then render can redraw the whole thing and you'd say:

suggestionsView.collection.reset(this.getSuggestions(query))
// ------------------------^^

to refresh things.


Some clarification from the comments: Models don't generate 'add' events, only collections trigger those. Models trigger 'change' events when their properties change, collections trigger 'add' and 'remove' events when models are added and removed (respectively) from them; collections can also trigger 'change' events because they forward all the events from their enclosed models:

Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience.

So if you want to use Collection#set then you'd want three handlers in your view:

  1. this.listenTo(this.collection, 'add', ...): A new model has been added to the collection so render it.
  2. this.listenTo(this.collection, 'remove', ...): A model has been removed from the collection so remove its part of the view.
  3. this.listenTo(this.collection, 'change', ...): A model has changed so update its part of the view.

If you're only working with small collections then reset might be less work. If your collections are larger or the view changes are more expensive then dealing with the three events separately might be best.

In any case, if you're using sub-views, you'll want to maintain a list of them somewhere in the parent view so that you can call remove on them to make sure things are properly cleaned up. If you're destroying the models when removing them from the collection, you could have the sub-views bind to their model's 'destroy' events and remove themselves as needed.

The Catalog of Events might be worth a review to see what events are triggered at which times.

like image 179
mu is too short Avatar answered Nov 15 '22 16:11

mu is too short