Backbone 子视图事件未触发,无法访问 this.model
Backbone subview event doesn't fire, access to this.model lost
我正在使用一个主视图和一个子视图,子视图有自己的子视图,但我在子子视图上丢失了事件。
查看 SO,看起来需要一个 delegateEvents,但我不知道如何或在哪里。
此外,必须将 tbody: tbodyEl[ "0" ].outerHTML
传递给我的模板似乎真的很 hacky,但我不知道这是否与事件问题有关。
非常感谢任何帮助。
主视图:
return Backbone.View.extend({
el: "#content",
initialize: function () {
this.render();
},
render: function () {
var todosView = new TodosView({
collection: projectCol
});
}
});
待办事项视图:
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
}));
}
});
待办事项:
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
}
});
10 月 25 日更新:感谢@WinterSoldier,这就是我最终得到的:代码构建元素在插入 DOM 之前完成,并且 todoView.events 有效,可以完全访问 this.model, 另外,看起来更 '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 );
}
} );
让我列出我根据问题所做的假设:
- 模板渲染没有问题。
- 模板代码可以替换为纯 html 以检查
事件触发。
基于这些假设,我创建了一个包含两个模型的集合并将它们传递给 TodosView,我从中迭代这些模型以生成行视图 - 然后将其添加到 'tbody' 标签。
请在此处关注 fiddle =>
https://jsfiddle.net/randomfifaguy/304kffed/1/
console.log("hello");
Test = Backbone.Model.extend({});
var testModel1 = new Test({
'task': 'taskA',
'due': 'dueByZ'
})
var testModel2 = new Test({
'task': 'taskB',
'due': 'dueByY'
})
console.log(testModel1.get('task'));
console.log(testModel2.get('task'));
ModelCollection = Backbone.Collection.extend({});
var models = new ModelCollection([testModel1, testModel2]);
console.log(models);
TodosView = Backbone.View.extend({
el: "#content-body",
initialize: function() {
this.render();
},
render: function() {
var templ = "<div class='table-responsive'><table id='todo-table'class='table'><span class='caption'>Top Tasks <a id='<%= project %>' class='projectName'>(See All)</span></a><thead><tr><th>Task</th><th>Due</th></tr></thead><tbody></tbody></table></div>";
$(this.el).html(templ);
_.each(this.collection.models, function(mdl){
var view = new TodoView({
model: mdl
});
$('tbody').append(view.el);
});
}
});
MainView = Backbone.View.extend({
el: "#content",
initialize: function() {
this.render();
},
render: function() {
new TodosView({
collection: models
});
}
});
TodoView = Backbone.View.extend({
tagName: "tr",
className: "todo-rec",
events: {
"click .edit": "editFields"
},
initialize: function() {
this.render();
this.updateContent();
},
render: function() {
var html = "<td><label id='task'class='edit tasklabel'></label><input id='edited-task'class='new-edit taskinput'style='display:none;' value=''></td><td><span id='due' class='edit duelabel'></span><input id='edited-due' class='new-edit dueinput' style='display:none;'value=''></td>"
this.$el.html(html);
return this;
},
editFields: function() {
console.log("todoView: editFields clicked");
},
updateContent: function() {
this.$el.find('.tasklabel').text(this.model.get('task'))
this.$el.find('.taskinput').val(this.model.get('task'))
this.$el.find('.duelabel').text(this.model.get('due'))
this.$el.find('.dueinput').val(this.model.get('due'))
}
});
var mainViewObj = new MainView();
<body>
<div id='content'>
Here is a sample div
<div id="content-body">
content to be changed
</div>
</div>
</body>
不过我想知道你的html最外层的看法,以帮助你更好。
请将它与 fiddle 的输出 html 进行比较并告诉我。
希望这能回答你的问题。
正在详细回答您的评论
您可能想要更改这段代码
//You might want to change this piece of code
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
}));
你的 tbodyEl 在你渲染之前基本上没有指向任何东西
在附加到 tbody
之前尝试这样做
this.$el.append(this.template({
project: projectName,
tbody: tbodyEl["0"].outerHTML
}));
// followed by
this.collection.each(function (item) {
var todoView = new TodoView({
model: item
});
tbodyEl.append(todoView.el);
});
我正在使用一个主视图和一个子视图,子视图有自己的子视图,但我在子子视图上丢失了事件。
查看 SO,看起来需要一个 delegateEvents,但我不知道如何或在哪里。
此外,必须将 tbody: tbodyEl[ "0" ].outerHTML
传递给我的模板似乎真的很 hacky,但我不知道这是否与事件问题有关。
非常感谢任何帮助。
主视图:
return Backbone.View.extend({
el: "#content",
initialize: function () {
this.render();
},
render: function () {
var todosView = new TodosView({
collection: projectCol
});
}
});
待办事项视图:
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
}));
}
});
待办事项:
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
}
});
10 月 25 日更新:感谢@WinterSoldier,这就是我最终得到的:代码构建元素在插入 DOM 之前完成,并且 todoView.events 有效,可以完全访问 this.model, 另外,看起来更 '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 );
}
} );
让我列出我根据问题所做的假设:
- 模板渲染没有问题。
- 模板代码可以替换为纯 html 以检查 事件触发。
基于这些假设,我创建了一个包含两个模型的集合并将它们传递给 TodosView,我从中迭代这些模型以生成行视图 - 然后将其添加到 'tbody' 标签。
请在此处关注 fiddle => https://jsfiddle.net/randomfifaguy/304kffed/1/
console.log("hello");
Test = Backbone.Model.extend({});
var testModel1 = new Test({
'task': 'taskA',
'due': 'dueByZ'
})
var testModel2 = new Test({
'task': 'taskB',
'due': 'dueByY'
})
console.log(testModel1.get('task'));
console.log(testModel2.get('task'));
ModelCollection = Backbone.Collection.extend({});
var models = new ModelCollection([testModel1, testModel2]);
console.log(models);
TodosView = Backbone.View.extend({
el: "#content-body",
initialize: function() {
this.render();
},
render: function() {
var templ = "<div class='table-responsive'><table id='todo-table'class='table'><span class='caption'>Top Tasks <a id='<%= project %>' class='projectName'>(See All)</span></a><thead><tr><th>Task</th><th>Due</th></tr></thead><tbody></tbody></table></div>";
$(this.el).html(templ);
_.each(this.collection.models, function(mdl){
var view = new TodoView({
model: mdl
});
$('tbody').append(view.el);
});
}
});
MainView = Backbone.View.extend({
el: "#content",
initialize: function() {
this.render();
},
render: function() {
new TodosView({
collection: models
});
}
});
TodoView = Backbone.View.extend({
tagName: "tr",
className: "todo-rec",
events: {
"click .edit": "editFields"
},
initialize: function() {
this.render();
this.updateContent();
},
render: function() {
var html = "<td><label id='task'class='edit tasklabel'></label><input id='edited-task'class='new-edit taskinput'style='display:none;' value=''></td><td><span id='due' class='edit duelabel'></span><input id='edited-due' class='new-edit dueinput' style='display:none;'value=''></td>"
this.$el.html(html);
return this;
},
editFields: function() {
console.log("todoView: editFields clicked");
},
updateContent: function() {
this.$el.find('.tasklabel').text(this.model.get('task'))
this.$el.find('.taskinput').val(this.model.get('task'))
this.$el.find('.duelabel').text(this.model.get('due'))
this.$el.find('.dueinput').val(this.model.get('due'))
}
});
var mainViewObj = new MainView();
<body>
<div id='content'>
Here is a sample div
<div id="content-body">
content to be changed
</div>
</div>
</body>
不过我想知道你的html最外层的看法,以帮助你更好。 请将它与 fiddle 的输出 html 进行比较并告诉我。 希望这能回答你的问题。
正在详细回答您的评论 您可能想要更改这段代码
//You might want to change this piece of code
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
}));
你的 tbodyEl 在你渲染之前基本上没有指向任何东西 在附加到 tbody
之前尝试这样做 this.$el.append(this.template({
project: projectName,
tbody: tbodyEl["0"].outerHTML
}));
// followed by
this.collection.each(function (item) {
var todoView = new TodoView({
model: item
});
tbodyEl.append(todoView.el);
});