I have a collection of models that I wish to render in a table view. Each model should be represented by a single row in the table, and this row should be generated using a template. I should be able to attach event handlers to that row (say click), that upon event alert some specific information regarding the model associated with that row.
A common way I've seen similar things to this done is to break each row out into it's own view, and have a parent view (lets say the table in this case) use the row view to generate the html to include in your code. However I can't figure out how this works with templates.
In this case, I can not attach events specifically to the RowView as it has no reference to a dom element (this.el
for backbone), it simply returns a string. How can I achieve what I want, whilst using a template to maximum capacity?
The question isn't specifically about events, templating or using nested views, but more about the right way to use Backbone to achieve this kind of output.
Sample code(also in a fiddle):
/** View representing a table */
var TableView = Backbone.View.extend({
tagName: 'table',
render: function() {
var rows = _.map(this.collection.models, function(p) {
return new RowView({model: p}).render();
});
$('body').html(this.$el.html(rows.join('')));
}
});
/** View representing a row of that table */
var RowView = Backbone.View.extend({
render: function() {
// imagine this is going through a template, but for now
// lets just return straight html.
return '<tr>' +
'<td>' + this.model.get('name') + '</td>' +
'<td>' + this.model.get('age') + '</td>' +
'</tr>';
}
});
var data = [
{'name': 'Oli', 'age': 25},
{'name': 'Sarah', 'age': 20}];
/** Collection of models to draw */
var peopleCollection = new Backbone.Collection(data);
var tableView = new TableView({collection: peopleCollection});
tableView.render();
Thank you!
One way to handle a hierarchy of views is to have each view render its children and append them to its el
. The events are then handled by each view, according to its model/collection.
To inject your HTML as the view el
and thus control the container element, you can use the setElement method
setElement
view.setElement(element)
If you'd like to apply a Backbone view to a different DOM element, use setElement, which will also create the cached $el reference and move the view's delegated events from the old element to the new one.
Your example could be rewritten as
var rowTemplate=_.template("<tr>"+
"<td class='name'><%= name %></td>"+
"<td class='age'><%= age %></td>"+
"</tr>");
/** View representing a table */
var TableView = Backbone.View.extend({
tagName: 'table',
initialize : function() {
_.bindAll(this,'render','renderOne');
},
render: function() {
this.collection.each(this.renderOne);
return this;
},
renderOne : function(model) {
var row=new RowView({model:model});
this.$el.append(row.render().$el);
return this;
}
});
/** View representing a row of that table */
var RowView = Backbone.View.extend({
events: {
"click .age": function() {console.log(this.model.get("name"));}
},
render: function() {
var html=rowTemplate(this.model.toJSON());
this.setElement( $(html) );
return this;
}
});
var data = [
{'name': 'Oli', 'age': 25},
{'name': 'Sarah', 'age': 20}];
/** Collection of models to draw */
var peopleCollection = new Backbone.Collection(data);
var tableView = new TableView({collection: peopleCollection});
$("body").append( tableView.render().$el );
And a Fiddle http://jsfiddle.net/9avm6/5/