Backbone.js is basically an uber-light framework that allows you to structure your Javascript code in an MVC (Model, View, Controller) fashion where...
Model is part of your code that retrieves and populates the data,
View is the HTML representation of this model (views change as models change, etc.)
and optional Controller that in this case allows you to save the state of your Javascript application via a hashbang URL, for example: http://twitter.com/#search?q=backbone.js
Some pros that I discovered with Backbone:
No more Javascript Spaghetti: code is organized and broken down into semantically meaningful .js files which are later combined using JAMMIT
No more jQuery.data(bla, bla)
: no need to store data in DOM, store data in models instead
event binding just works
extremely useful Underscore utility library
backbone.js code is well documented and a great read. Opened my eyes to a number of JS code techniques.
Cons:
- Took me a while to wrap my head around it and figure out how to apply it to my code, but I'm a Javascript newbie.
Here is a set of great tutorials on using Backbone with Rails as the back-end:
CloudEdit: A Backbone.js Tutorial with Rails:
http://www.jamesyu.org/2011/01/27/cloudedit-a-backbone-js-tutorial-by-example/
http://www.jamesyu.org/2011/02/09/backbone.js-tutorial-with-rails-part-2/
p.s. There is also this wonderful Collection class that lets you deal with collections of models and mimic nested models, but I don't want to confuse you from the start.
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)
var User = 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 );
}
});
var Users = Backbone.Collection.extend({
model: User,
cleanUp: function( data ){
this.each( function( model ) { model.unlink(); } );
this.reset( data );
}
});
// testing
var users = new Users([{name: 'John'}]);
console.log('users.size: ', users.size()); // 1
AppEvents.trigger('my_event'); // John still listening...
users.cleanUp();
console.log('users.size: ', users.size()); // 0
AppEvents.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.
Best Answer
A similar solution to what @Andreas proposed but without Backbone.Marionette (heavily inspired nonetheless, see the article linked in the question).
All you have to do is to define a module that creates a singleton of an event listener and require this object in the modules where you want to trigger an event or listen to this event.
Let's say you have app/channel.js defining your channel
You can then use it as a listener via
and you can trigger an event on this channel via