This time I'm struggling with the different methods to bind events. I've all the mentioned methods in my code. I just don't know, if I'm on the right way. Maybe I should always use bindTo to ensure that my views are closed completely after a change (at present this would often yield errors)? Are there any best practices which will help me to get in the right direction?
To illustrate my current understanding of Marionette, here's one module from my app. As always, every hint is very welcome.
PP.module('Grid', function(Grid, PP, Backbone, Marionette, $, _){
Grid.Product = Backbone.Model.extend({});
Grid.ProductCollection = Backbone.Collection.extend({
model: Grid.Product,
url: '/products/query'
});
Grid.ProductView = Backbone.Marionette.ItemView.extend({
className: 'grid',
template: 'public/templates/grid-template'
});
// Helper Methods
// -----------------------
var getGenderCode = function(genderName){
var genderMap = {
'men': 'M',
'women': 'W',
'unisex': 'A'
}
return genderMap.genderName;
}
// Public API
// -------------------
Grid.renderGrid = function(productCollection){
Grid.productView = new Grid.ProductView({
collection: productCollection
});
Grid.productView.bind('show', function(){
$('#isotope-container').isotope({
itemSelector : '.item',
containerStyle: {
position: 'relative',
zIndex: 1
}
});
});
PP.Layout.mainRegion.show(Grid.productView);
}
// Event Handlers
// -----------------------
PP.vent.bind('grid:requested', function(categoryData){
// use bootstrapped data on first start
if (PP.App.bootstrappedCategoryName !== categoryData.categoryName) {
Grid.productCollection.fetch({
data: {
gender: getGenderCode(categoryData.categoryName),
category: categoryData.categoryId
}
});
}
else {
PP.vent.trigger('mainview:ready', {
categoryName: PP.App.bootstrappedCategoryName
});
}
});
// Initializer
// --------------------
PP.addInitializer(function(options){
Grid.productCollection = new Grid.ProductCollection();
Grid.productCollection.on('reset', function(){
Grid.renderGrid(Grid.productCollection);
});
Grid.productCollection.reset(options.newArrivalsList);
});
});
the general guidelines is that any time you have an object that is created and destroyed throughout the life of the application, and that object needs to bind to events from some other object, you should use the EventBinder
.
Views are the perfect example of this. Views get created and destroyed all the time. They also bind to a lot of different events from both the model
and collection
on the view. When the view is destroyed, those events need to be cleaned up. By using the built-in bindTo
method on the view, the event handlers will be cleaned up for you. If you don't use bindTo
and instead use on
directly, you'll have to manually call off
to unbind the events when the view is closed / destroyed. If you don't, you'll end up with zombies (memory leaks).
If you haven't read them yet, check out these articles:
http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
http://lostechies.com/derickbailey/2012/03/19/backbone-js-and-javascript-garbage-collection/
Views are not the only place that this applies, though, and not the only use case for EventBinder
.
If you are working with handful of objects, binding to their events, and those objects can be replaced with other object instances, then an EventBinder
would be useful. In this case, think of the EventBinder as a collection or group of events for related objects.
Let's say you have ObjA, ObjB and ObjC. Each of these fires some events, and you want to make sure you clean up the event handlers when your code is done. This is easy with an EventBinder
:
doStuff = {
start: function(a, b, c){
this.events = new Marionette.EventBinder();
this.events.bindTo(a, "foo", this.doSomething, this);
this.events.bindTo(b, "bar", this.anotherThing, this);
this.events.bindTo(c, "baz", this.whatever, this);
},
doSomething: function(){ /* ... */ },
anotherThing: function(){ /* ... */ },
whatever: function(){ /* ... */ },
stop: function(){
this.events.unbindAll();
}
}
doStuff.start(ObjA, ObjB, ObjC);
// ... some time later in the code, stop it
doStuff.stop();
Calling stop
in this code will properly clean up all the event handlers for this use.
on
/off
DirectlyThe converse to all of this, is to say that you don't always need to use an EventBinder
. You can get away without it, always. But you need to remember to clean up your events when necessary.
In situations that do not need event handlers to be cleaned up, there is no need to use an EventBinder
, as well. This may be an event that a router triggers, or that the Marionette.Application
object triggers. For those application lifecycle events, or events that need to live throughout the entire life of the app, don't bother with an EventBinder
. Instead, just let the event handlers live on. When the person refreshes the browser window, or navigates to a different site, the handlers will be cleaned up at that point.
But when you need to manage your memory and event handlers, cleaning up references and closing things down without refreshing the page or navigating away from it, then EventBinder
becomes important as it simplifies event management.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With