为什么 Backbone.Collection 对第一个观众显示了两次?
Why is Backbone.Collection shown twice for the first viewer?
我尝试通过修改Todo演示,在同一页面的两个不同查看器中显示相同的数据。任何数据更改都将反映在两个查看器中。该代码仅适用于一个查看器,但在使用 fetch 重新加载页面后显示了我不想要的结果。待办事项列表在第一个查看器中显示两次,并且一半的结果松散了绑定事件。有没有人可以帮助我?提前致谢。
The code can be played with the jsfiddle.
HTML:
<div id="todoapp">
<header>
<h1>Todos</h1>
<input class="new-todo" type="text" placeholder="What needs to be done?">
</header>
<section class="main">
<input id=toggle-all class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list"></ul>
</section>
<footer>
<a class="clear-completed">Clear completed</a>
<div class="todo-count"></div>
</footer>
</div>
<div id="todoapp2">
<header>
<h1>Todos</h1>
<input class="new-todo" type="text" placeholder="What needs to be done?">
</header>
<section class="main">
<input id=toggle-all-2 class="toggle-all" type="checkbox">
<label for="toggle-all-2">Mark all as complete</label>
<ul class="todo-list"></ul>
</section>
<footer>
<a class="clear-completed">Clear completed</a>
<div class="todo-count"></div>
</footer>
</div>
<div id="instructions">
Double-click to edit a todo.
</div>
<!-- Templates -->
<script type="text/template" id="item-template">
<div class="view">
<input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<label><%- title %></label>
<a class="destroy"> Destroy </a>
</div>
<input class="edit" type="text" value="<%- title %>" />
</script>
<script type="text/template" id="stats-template">
<% if (done) { %>
<a class="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
<% } %>
<div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
</script>
JS:
$(function () {
Todo = Backbone.Model.extend({
defaults: function() {
return {
title: "empty todo...",
order: Todos.nextOrder(),
done: false
};
},
toggle: function() {
this.save({done: !this.get("done")});
}
});
TodoList = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage("todos-backbone"),
done: function() {
return this.where({done: true});
},
remaining: function() {
return this.where({done: false});
},
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
comparator: 'order'
});
var Todos = new TodoList;
TodoView = Backbone.View.extend({
tagName: "li",
template: _.template( $('#item-template').html() ),
events: {
"click .toggle" : "toggleDone",
"dblclick .view" : "edit",
"click a.destroy" : "clear",
"keypress .edit" : "updateOnEnter",
"blur .edit" : "close"
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
this.$el.html( this.template(this.model.toJSON()) );
this.$el.toggleClass('done', this.model.get('done') );
this.input = this.$('.edit');
//alert(this.input.val() );
return this;
},
toggleDone: function() {
this.model.toggle();
},
edit: function() {
this.$el.addClass("editing");
this.input.focus();
},
close: function() {
var value = this.input.val();
if (!value) {
this.clear();
} else {
this.model.save({title: value});
this.$el.removeClass("editing");
}
},
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
clear: function() {
this.model.destroy();
}
});
var AppView = Backbone.View.extend({
//el: $("#todoapp"),
statsTemplate: _.template($('#stats-template').html()),
events: {
"keypress .new-todo": "createOnEnter",
"click .clear-completed": "clearCompleted",
"click .toggle-all": "toggleAllComplete"
},
initialize: function() {
this.input = this.$(".new-todo");
this.allCheckbox = this.$(".toggle-all")[0];
this.listenTo(Todos, 'add', this.addOne);
this.listenTo(Todos, 'reset', this.addAll);
this.listenTo(Todos, 'all', this.render);
this.footer = this.$('footer');
this.main = this.$('.main');
//Todos.fetch();
Todos.fetch({reset:true});
},
render: function() {
var done = Todos.done().length;
var remaining = Todos.remaining().length;
if (Todos.length) {
this.main.show();
this.footer.show();
this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
this.allCheckbox.checked = !remaining;
},
addOne: function(todo) {
var view = new TodoView({model: todo});
//console.log(view.render().el );
//this.$(".todo-list").append( view.render().el );
this.$(".todo-list").append( view.render().el );
console.log( this.main.parent() );
},
addAll: function() {
Todos.each(this.addOne, this);
},
createOnEnter: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val()) return;
Todos.create({title: this.input.val()});
this.input.val('');
},
clearCompleted: function() {
_.invoke(Todos.done(), 'destroy');
return false;
},
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
Todos.each(function (todo) { todo.save({'done': done}); });
}
});
var app1 = new AppView({ el: $("#todoapp") });
var app2 = new AppView({ el: $("#todoapp2") });
});
原因是您只为两个视图使用了一个集合。
- 最初创建了 app1,并在初始化时获取了集合,触发事件仅由 app1 侦听
- 现在创建了第二个视图并再次获取集合,但是现在在 app1 和 app2 中都监听了集合触发的重置事件,并且都被渲染了
为了解决这个问题,您可以在两个视图都初始化后只获取一次集合。
我尝试通过修改Todo演示,在同一页面的两个不同查看器中显示相同的数据。任何数据更改都将反映在两个查看器中。该代码仅适用于一个查看器,但在使用 fetch 重新加载页面后显示了我不想要的结果。待办事项列表在第一个查看器中显示两次,并且一半的结果松散了绑定事件。有没有人可以帮助我?提前致谢。
The code can be played with the jsfiddle.
HTML:
<div id="todoapp">
<header>
<h1>Todos</h1>
<input class="new-todo" type="text" placeholder="What needs to be done?">
</header>
<section class="main">
<input id=toggle-all class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list"></ul>
</section>
<footer>
<a class="clear-completed">Clear completed</a>
<div class="todo-count"></div>
</footer>
</div>
<div id="todoapp2">
<header>
<h1>Todos</h1>
<input class="new-todo" type="text" placeholder="What needs to be done?">
</header>
<section class="main">
<input id=toggle-all-2 class="toggle-all" type="checkbox">
<label for="toggle-all-2">Mark all as complete</label>
<ul class="todo-list"></ul>
</section>
<footer>
<a class="clear-completed">Clear completed</a>
<div class="todo-count"></div>
</footer>
</div>
<div id="instructions">
Double-click to edit a todo.
</div>
<!-- Templates -->
<script type="text/template" id="item-template">
<div class="view">
<input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<label><%- title %></label>
<a class="destroy"> Destroy </a>
</div>
<input class="edit" type="text" value="<%- title %>" />
</script>
<script type="text/template" id="stats-template">
<% if (done) { %>
<a class="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
<% } %>
<div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
</script>
JS:
$(function () {
Todo = Backbone.Model.extend({
defaults: function() {
return {
title: "empty todo...",
order: Todos.nextOrder(),
done: false
};
},
toggle: function() {
this.save({done: !this.get("done")});
}
});
TodoList = Backbone.Collection.extend({
model: Todo,
localStorage: new Backbone.LocalStorage("todos-backbone"),
done: function() {
return this.where({done: true});
},
remaining: function() {
return this.where({done: false});
},
nextOrder: function() {
if (!this.length) return 1;
return this.last().get('order') + 1;
},
comparator: 'order'
});
var Todos = new TodoList;
TodoView = Backbone.View.extend({
tagName: "li",
template: _.template( $('#item-template').html() ),
events: {
"click .toggle" : "toggleDone",
"dblclick .view" : "edit",
"click a.destroy" : "clear",
"keypress .edit" : "updateOnEnter",
"blur .edit" : "close"
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
this.$el.html( this.template(this.model.toJSON()) );
this.$el.toggleClass('done', this.model.get('done') );
this.input = this.$('.edit');
//alert(this.input.val() );
return this;
},
toggleDone: function() {
this.model.toggle();
},
edit: function() {
this.$el.addClass("editing");
this.input.focus();
},
close: function() {
var value = this.input.val();
if (!value) {
this.clear();
} else {
this.model.save({title: value});
this.$el.removeClass("editing");
}
},
updateOnEnter: function(e) {
if (e.keyCode == 13) this.close();
},
clear: function() {
this.model.destroy();
}
});
var AppView = Backbone.View.extend({
//el: $("#todoapp"),
statsTemplate: _.template($('#stats-template').html()),
events: {
"keypress .new-todo": "createOnEnter",
"click .clear-completed": "clearCompleted",
"click .toggle-all": "toggleAllComplete"
},
initialize: function() {
this.input = this.$(".new-todo");
this.allCheckbox = this.$(".toggle-all")[0];
this.listenTo(Todos, 'add', this.addOne);
this.listenTo(Todos, 'reset', this.addAll);
this.listenTo(Todos, 'all', this.render);
this.footer = this.$('footer');
this.main = this.$('.main');
//Todos.fetch();
Todos.fetch({reset:true});
},
render: function() {
var done = Todos.done().length;
var remaining = Todos.remaining().length;
if (Todos.length) {
this.main.show();
this.footer.show();
this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
} else {
this.main.hide();
this.footer.hide();
}
this.allCheckbox.checked = !remaining;
},
addOne: function(todo) {
var view = new TodoView({model: todo});
//console.log(view.render().el );
//this.$(".todo-list").append( view.render().el );
this.$(".todo-list").append( view.render().el );
console.log( this.main.parent() );
},
addAll: function() {
Todos.each(this.addOne, this);
},
createOnEnter: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val()) return;
Todos.create({title: this.input.val()});
this.input.val('');
},
clearCompleted: function() {
_.invoke(Todos.done(), 'destroy');
return false;
},
toggleAllComplete: function () {
var done = this.allCheckbox.checked;
Todos.each(function (todo) { todo.save({'done': done}); });
}
});
var app1 = new AppView({ el: $("#todoapp") });
var app2 = new AppView({ el: $("#todoapp2") });
});
原因是您只为两个视图使用了一个集合。
- 最初创建了 app1,并在初始化时获取了集合,触发事件仅由 app1 侦听
- 现在创建了第二个视图并再次获取集合,但是现在在 app1 和 app2 中都监听了集合触发的重置事件,并且都被渲染了
为了解决这个问题,您可以在两个视图都初始化后只获取一次集合。