Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone - Why doesn't a collection.reset trigger a model event?

I'm curious to find out why resetting a backbone collection doesn't fire a model event. However, it seems only logical to fire a model event when a model is physically being removed from a collection.

Is this intentional or am I missing something? If backbone doesn't do this sort of thing what's a good practice for delegating events like so.

Why does backbone not trigger a model event when its collection resets?

var TicketModel = Backbone.Model.extend({
    defaults: {
        name: 'crafty',
        email: '[email protected]'
    },
    initialize: function(){
        this.on("all", function(event){
            console.log(event)
        });
    }

});

var TicketCollection = Backbone.Collection.extend({
    model: TicketModel,

    });


var tickets = new TicketCollection([
    {
        name: 'halldwq'
    },
    {
        name: 'dascwq'
    },
    {
        name: 'dsacwqe'
    }

]);

tickets.reset();
like image 631
Julian Krispel-Samsel Avatar asked Aug 02 '12 09:08

Julian Krispel-Samsel


2 Answers

This is the backbone reset function:

reset: function(models, options) {
  models  || (models = []);
  options || (options = {});
  for (var i = 0, l = this.models.length; i < l; i++) {
    this._removeReference(this.models[i]);
  }
  this._reset();
  this.add(models, _.extend({silent: true}, options));
  if (!options.silent) this.trigger('reset', this, options);
  return this;
},

We can ignore the last 3 lines because you don't supply any models to the reset-function. Also let's ignore the first 2 lines as well. So first we loop through the models in this collection and call the collection's _removeReference(model) method, it looks like this:

_removeReference: function(model) {
  if (this == model.collection) {
    delete model.collection;
  }
  model.off('all', this._onModelEvent, this);
},

What happens here is that we're removing the collection-property from the model object altogether and also remove the binding to this model's events. Next up we call the collection's _reset()-function, that looks like this:

_reset: function(options) {
  this.length = 0;
  this.models = [];
  this._byId  = {};
  this._byCid = {};
}, 

It just outright removes any reference to any models the collection has ever had.

What can we make from this? Well the collection reset -function in Backbone basically just circumvents all the official channels of removing models and does it all in hush hush secrecy, causing no other events than reset to be fired. So you want to fire the model's remove event for every model removed from a collection during reset? Easy! Just overwrite Backbone.Collection's reset-function like this:

var Collection = Backbone.Collection.extend({
  reset: function(models, options) {
    models  || (models = []);
    options || (options = {});

    for (var i = 0, l = this.models.length; i < l; i++) {
      this._removeReference(this.models[i]);
      // trigger the remove event for the model manually
      this.models[i].trigger('remove', this.models[i], this);
    }

    this._reset();
    this.add(models, _.extend({silent: true}, options));
    if (!options.silent) this.trigger('reset', this, options);
    return this;
  }
});

Hope this helps!

like image 157
jakee Avatar answered Sep 26 '22 02:09

jakee


Overriding Backbone method can cause pain when updating to another version.

Backbone stores an array of the models before the reset in options.previousModels, so just listen to the reset event and trigger a 'remove' event on those previous models:

collection.on('reset', function(col, opts){
   _.each(opts.previousModels, function(model){
        model.trigger('remove');
    });
});

That'd do the trick.

like image 24
Julian Krispel-Samsel Avatar answered Sep 26 '22 02:09

Julian Krispel-Samsel