How to bind events for non-instance Backbone view modules.
If you've ever developed an application using RequireJS (or any modularizing technique) and Backbone, and if you're accustomed to building views in such a way that their el
attributes point to a parent element rather than themselves and some parent object is the one assigning the views' models and el
s, you may have discovered a weakness in Backbone's event binding mechanism. Here's some troublesome code:
define([ 'jquery'
, 'underscore'
, 'backbone'
, 'text!views/filter-tag.html'
], function($, _, Backbone, filterTag){
return Backbone.View.extend({
render: function(){
var self = this;
this.$tag = $(filterTag);
this.$el.append(self.$tag);
},
events: {
'click': 'select'
},
select: function(e){
console.log(this);
}
});
});
Notice that the module here doesn't return a Backbone view instance, it just returns the extended Backbone view for some other module to make instances from.
With such code, you'll discover that if you click on any views created by this module, every view created by this module will call select
. It's really a mess.
This is because of Backbone's mechanism for binding events. I'd explain it, but if you're really curious you'll read the beautifully documented source code.
Anyway, what you'll need to do is bind the event yourself. Don't worry, it's not that big of a deal. Get rid of the events
object, add this.bindEvents()
to the render function, and then add the bindEvents
function that should look like this:
bindEvents: function(){
var self = this;
this.$tag.on('click', function(){
self.select.apply(self, arguments)
});
}
This way each instance of the view has events bound to the individual instance.
Using apply
allows event
, etc to be passed to your callback (which is in this case, for the sake of example, a function called select
).
The full, functioning example looks like this:
define([ 'jquery'
, 'underscore'
, 'backbone'
, 'text!views/filter-tag.html'
], function($, _, Backbone, filterTag){
return Backbone.View.extend({
render: function(){
var self = this;
this.$tag = $(filterTag);
this.$el.append(self.$tag);
this.bindEvents();
},
bindEvents: function(){
var self = this;
this.$tag.on('click', function(){
self.select.apply(self, arguments)
});
},
select: function(e){
console.log(this);
}
});
});
Written by Will Shown
Related protips
2 Responses
Much thanks! I was pounding my head against this because my view tag was a tr as part of a table and needed a click event to fire on the entire tr. Can't really just wrap that with a div, if you know what I mean.
Glad this helped you!