Reputation: 45081
Let's say I have some items to show in a list. The list has a view that aggregates all the items as item views. Now I want to handle the click events on the item views and I delegate the handling to the list view.
Let's see some example code:
ItemView = Backbone.View.extend({
className: 'item',
initialize: function() {
this.$el.data('backbone-view', this);
}
});
Note that I am attaching the view object itself as a property of the root element, which essentially creates a circular reference situation for the view and the element.
ListView = Backbone.View.extend({
initialize: function() {
// contains the item views
this.items = [];
// click event delegation
this.$el.click(_.bind(this._onClick, this));
},
addItem: function(v) {
if ( !(v instanceof ItemView) ) return;
this.items.push(v);
this.$el.append(v.el);
},
_onClick: function(e) {
var el = $(e.target).closest('.item'),
view = el.data('backbone-view');
// do something with the view
}
});
This is a very general pattern whenever one has to deal with any kind of list views.
I am getting the item view back in the handler via the data property that I set on the item on the initialization time. I need to get item view because anything that I want to do on the item as part of handling the click event is based on the view.
Also note that I am using closest
because item view may be complex and the actual target of the click event may be a descendant of the root element.
So the question: is this way to binding the view to it's root element via data
properties the right approach -- in particular when considering garbage collection and memory leaks? Can there be something better than this?
Upvotes: 3
Views: 929
Reputation: 35890
You should catch the events in the child view. In my opinion, any Backbone view should only handle the DOM events of its element and its children. If views are nested, as yours are, the most specific view should handle the events.
If you want to delegate handling to the parent view, you can trigger a backbone event in the ItemView
, and listen to those in the ListView
.
ItemView = Backbone.View.extend({
events: {
"click":"onClick"
},
onClick: function() {
//trigger a custom event, passing the view as first argument
this.trigger('click', this);
}
});
ListView = Backbone.View.extend({
addItem: function(v) {
if ( !(v instanceof ItemView) ) return;
//listen to custom event
this.listenTo(v, 'click', this._onClick);
this.items.push(v);
this.$el.append(v.el);
},
_onClick:function(itemView) {
//...
}
});
If the click
event represents some "higher level" action, such as select
or activate
, you should name your custom events as such. This way you can create a logical, robust interface between your views without concerning the parent ListView
with the implementation details of its child. Only ItemView
should know that whether it's been clicked, hovered, double clicked etc.
Upvotes: 1