I'm using a master view with a subview with it's own subview, and I am losing the event on the sub-sub view.
Looking in SO, it looks like a delegateEvents is needed, but I can't figure out how or where.
Also, it seems really hacky to have to pass tbody: tbodyEl[ "0" ].outerHTML
to my template, but I don't know if it's related to the event issue.
Any help greatly appreciated.
mainView:
return Backbone.View.extend({
el: "#content",
initialize: function () {
this.render();
},
render: function () {
var todosView = new TodosView({
collection: projectCol
});
}
});
todosView:
return Backbone.View.extend({
el: "#content-body",
template: _.template([
'<div class="table-responsive">',
'<table id="todo-table" class="table">',
'<span class="caption">Top <%= project %> Tasks <a id="<%= project %>" class="projectName">(See All)</span></a>',
'<thead>',
'<tr>',
'<th>Task</th>',
'<th>Due</th>',
'</tr>',
'</thead>',
'<%= tbody %>',
'</table>',
'</div>'
].join("")),
initialize: function () {
this.render();
},
render: function () {
var projectName = this.collection.models[0].attributes.project;
var tbodyEl = $("<tbody />");
this.collection.each(function (item) {
var todoView = new TodoView({
model: item
});
tbodyEl.append(todoView.el);
});
this.$el.append(this.template({
project: projectName,
tbody: tbodyEl["0"].outerHTML
}));
}
});
todoView:
return Backbone.View.extend({
tagName: "tr",
className: "todo-rec",
template: _.template([
"<td>",
"<label id='task' class='edit'><%= task %></label>",
"<input id='edited-task' class='new-edit' style='display:none;' value='<%= task %>'>",
"</td>",
"<td>",
"<span id='due' class='edit'><%= due %></span>",
"<input id='edited-due' class='new-edit' style='display:none;' value='<%= due %>'>",
"</td>",
].join("")),
events: {
"click .edit": "editFields"
},
initialize: function () {
this.render();
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
editFields: function () {
console.log("todoView: editFields clicked"); // <-- does not fire
}
});
25 Oct Updated: Thanks to @WinterSoldier, this is what I ended up with: code build elements complete before inserting into DOM, and todoView.events works, giving full access to this.model, plus, it looks more 'Backbone-ish':
// mainView:
return Backbone.View.extend( {
el: "#content",
initialize: function(){
this.render();
},
render: function(){
$( "#content-body" ).empty();
var section = new TodosView( { collection: projectCol } ); // should be returning a section div
$( "#content-body" ).append( section.el );
}
} );
// todosView:
return Backbone.View.extend( {
// used tagName so the element can be built complete before inserting into DOM
tagName: "div",
className: "table-responsive",
// tbody changed to empty element
template: _.template( [
'<table id="todo-table" class="table">',
'<span class="caption">Top <%= project %> Tasks <a id="<%= project %>" class="projectName">(See All)</span></a>',
'<thead>',
'<tr>',
'<th>Comp.</th>',
'<th>Task</th>',
'<th>Due</th>',
'<th>Priority</th>',
'<th>Delegated To</th>',
'<th>Project</th>',
'<th>Del.</th>',
'</tr>',
'</thead>',
'<tbody id="tbodyContent"></tbody>',
'</table>' ].join( "" )
),
initialize: function(){
this.render();
},
render: function(){
// new: render the template first, then append rows in #tbody
var projectName = this.collection.models[ 0 ].attributes.project;
this.$el.empty().html( this.template( {
project: projectName
} ) );
var this2 = this;
this.collection.each( function( item ){
var todoView = new TodoView( {model: item} );
this2.$el.find( "#tbodyContent" ).append( todoView.el );
} );
// now returning a <div class="table-responsive" with all rows
return this;
}
} );
// todoView:
// Note: nothing changed from original code
return Backbone.View.extend( {
tagName: "tr",
className: "todo-rec",
template: _.template( [
"<td>",
"<label id='task' class='edit'><%= task %></label>",
"<input id='edited-task' class='new-edit' style='display:none;' value='<%= task %>'>",
"</td>",
"<td>",
"<span id='due' class='edit'><%= due %></span>",
"<input id='edited-due' class='new-edit' style='display:none;' value='<%= due %>'>",
"</td>",
].join( "" )
),
events: {
"click .edit": "editFields"
},
initialize: function() {
this.render();
},
render: function() {
this.$el.html( this.template( this.model.toJSON() ) );
return this;
},
editFields: function() {
console.log( "todoView: editFields clicked", this );
}
} );
let me list out the assumptions I made based on the question:
Based on those assumption, I created a collection with two models and passed them over to TodosView from which I iterated over those models to generate row view- there by adding it to 'tbody' tag.
Please follow the fiddle here => https://jsfiddle.net/randomfifaguy/304kffed/1/
However I'd like to know your html of the outermost view to help you better. Please compare it with the output html of the fiddle and let me know. Hope that answers your question.
Answering your comment in detail You might want to change this piece of code
Your tbodyEl basically points to nothing until you render try doing this before you append to tbody