Backbone.js: How To Unbind From Events, On Model Remove
Solution 1:
I see that even when the binding event direction is this way Object1 -> listening -> Object2 it has to be removed in order to Object1 lost any alive reference.
And seeing that listening to the Model remove
event is not a solution due it is not called in a Collection.reset()
call then we have two solutions:
1. Overwrite normal Collection cleanUp
As @dira sais here you can overwrite Collection._removeReference
to make a more proper cleaning of the method.
I don't like this solutions for two reasons:
- I don't like to overwrite a method that has to call
super
after it. - I don't like to overwrite private methods
2. Over-wrapping your Collection.reset()
calls
Wich is the opposite: instead of adding deeper functionality, add upper functionality.
Then instead of calling Collection.reset()
directly you can call an implementation that cleanUp the models before been silently removed:
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
A sorter version of your code can looks like this:
AppEvents = {};
_.extend(AppEvents, Backbone.Events)
varUser = Backbone.Model.extend({
initialize: function(){
AppEvents.on('my_event', this.listen, this);
},
listen: function(){
console.log("%s still listening...", this.get('name'));
},
unlink: function(){
AppEvents.off( null, null, this );
}
});
varUsers = Backbone.Collection.extend({
model: User,
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
});
// testingvar users = newUsers([{name: 'John'}]);
console.log('users.size: ', users.size()); // 1AppEvents.trigger('my_event'); // John still listening...
users.cleanUp();
console.log('users.size: ', users.size()); // 0AppEvents.trigger('my_event'); // (nothing)
Check the jsFiddle.
Update: Verification that the Model is removed after remove the binding-event link
First thing we verify that Object1 listening to an event in Object2 creates a link in the direction Obect2 -> Object1:
In the above image we see as the Model (@314019) is not only retained by the users
collection but also for the AppEvents
object which is observing. Looks like the event linking for a programmer perspective is Object that listen -> to -> Object that is listened but in fact is completely the opposite: Object that is listened -> to -> Object that is listening.
Now if we use the Collection.reset()
to empty the Collection we see as the users
link has been removed but the AppEvents
link remains:
The users
link has disappear and also the link OurModel.collection
what I think is part of the Collection._removeReference()
job.
When we use our Collection.cleanUp()
method the object disappear from the memory, I can't make the Chrome.profile
tool to explicitly telling me the object @314019 has been removed but I can see that it is not anymore among the memory objects.
Solution 2:
I think the clean references process is a tricky part of Backbone
.
When you remove a Model
from a Collection
the Collection takes care to unbind
any event on the Model that the Collection its self is binding. Check this private Collection method.
Maybe you can use such a same technique in your Aggregator:
// ... Aggregator code
the_model.on( "remove", this.unlinkModel, this );
// ... more Aggregator code
unlinkModel: function( model ){
model.off( null, null, this );
}
This is in the case the direction of the binding is Aggregator -> Model. If the direction is the opposite I don't think you have to make any cleaning after Model removed.
Solution 3:
Instead of wrapping Collection
's reset
with cleanUp
as fguillen suggested, I prefer extending Collection
and overriding reset
directly. The reason is that
cleanUp
takes effect only in client's code, but not in library(i.e. Backbone)'s.
For example, Collection.fetch
may internally call Collection.reset
. Unless modifying the Backbone's source code, we cannot unbind models from events(as in cleanUp
) after calling Collection.fetch
.
Basically, my suggested snippet is as follows:
varMyCollection = Backbone.Collection.extend({
reset: function(models, options) {
this.each(function(model) {
model.unlink(); // same as fguillen's code
});
Backbone.Collection.prototype.reset.apply(this, arguments);
}
});
Later, we can create new collections based on MyCollection
.
Post a Comment for "Backbone.js: How To Unbind From Events, On Model Remove"