これがあなたのコードの簡単な実行での私の亀裂です。私は何もテストしていないので、タイプミスがあるかもしれません。迷子の空のモデルがどこから来ているのかはまだわかりませんが、以下に概説するようにアプリケーションを再構築すると、問題はなくなると思います。
モデルとコレクションは大丈夫ですので、あなたの見解を見てみましょう。
el: $('#todos'),
listBlock: $('#todos-list'),
newTodoField: $('#add input'),
//...
template: $('#todo-template').html(),
//...
events: { /* ... */ },
これらは問題ないはずですが、ビューの「クラス」がロードされるときに、これらすべての要素がDOMにあることを確認する必要があります。通常、テンプレートは1回コンパイルします。
template: _.template($('#todo-template').html()),
次にthis.template、HTMLを取得するための関数として使用します。templateこれは、以下のコンパイル済みテンプレート関数であると想定します。
initialize: function () {
_this = this;
ここに偶発的なグローバル変数があります。これは興味深いバグを引き起こす可能性があります。あなたは言いたいですvar _this = this;。
this.el = $(this.el);
バックボーンはすでにjQueryバージョンのelinを提供し$elているので、これを行う必要はありません。を使用するだけthis.$elです。
this.collection.fetch({
success : function(collection, response) {
_.each(response, function(i) {
var todo = new TodosModel({ /* ... */ });
// Add to collection
_this.collection.add(todo);
// Render
_this.render(todo);
});
},
//...
コレクションfetchは、ハンドラーが呼び出される前にモデルをコレクションに追加するsuccessため、新しいモデルを作成したり、コレクションに何かを追加したりする必要はありません。通常、このrenderメソッドは1つの部分だけをレンダリングするのではなく、全体をレンダリングし、ビューrenderをコレクションの"reset"イベントにバインドします。呼び出しは、フェッチしたときにイベントfetchをトリガーする"reset"ため、通常のパターンは次のようになります。
initialize: function() {
// So we don't have to worry about the context. Do this before you
// use `render` or you'll have reference problems.
_.bindAll(this, 'render');
// Trigger a call to render when the collection has some stuff.
this.collection.on('reset', this.render);
// And go get the stuff we want. You can put your `error` callback in
// here if you want it, wanting it is a good idea.
this.collection.fetch();
}
今のためにrender:
render: function (todo) {
var templ = _.template(this.template);
this.listBlock.append(templ({
id: todo.get('id'),
content: todo.get('content'),
completed: todo.get('completed')
}));
// Mark completed
if(todo.get('completed')) {
this.listBlock.children('li[data-id="'+todo.get('id')+'"]')
.addClass('todo-completed');
}
}
通常、これは2つの部分に分割されます。
renderコレクション全体をレンダリングします。
- たとえば
renderOne、単一のモデルをレンダリングする別の方法。renderOneこれにより、コレクションの"add"イベントにバインドすることもできます。
したがって、このようなものが一般的です。
render: function() {
// Clear it out so that we can start with a clean slate. This may or
// may not be what you want depending on the structure of your HTML.
// You might want `this.listBlock.empty()` instead.
this.$el.empty();
// Punt to `renderOne` for each item. You can use the second argument
// to get the right `this` or add `renderOne` to the `_.bindAll` list
// up in `initialize`.
this.collection.each(this.renderOne, this);
},
renderOne: function(todo) {
this.listBlock.append(
this.template({
todo: todo.toJSON()
})
)
// Mark completed
if(todo.get('completed')) {
this.listBlock.find('li[data-id="' + todo.id + '"]')
.addClass('todo-completed');
}
}
toJSONテンプレートにデータを提供するためにを使用していることに注意してください。バックボーンモデルとコレクションにはtoJSON、データの簡略化されたバージョンを提供する方法があるため、それを使用することもできます。モデルidは属性として使用できるため、モデルを取得するために使用する必要はありませんget。todo-completedロジックをテンプレートにプッシュすることができます(おそらくそうすべきです) 。
<% if(completed) { %>class="completed"<% } %>
適切な場所でトリックを行う必要があります。
addTodo: function (e) {
//...
var todo = new TodosModel({
id: todoID,
content: todoContent,
completed: todoCompleted
});
this.render(todo);
todo.save();
_this.collection.add(todo);
renderOneコレクションのイベントにバインドして"add"、新しいモデルのレンダリングを処理できます。次に、saveコールバックを使用して終了します。
var _this = this;
var todo = new TodosModel({ /* ... */ });
todo.save({}, {
wait: true,
success: function(model, response) {
// Let the events deal with rendering...
_this.collection.add(model);
}
});
繰り返しになりますが、のerrorコールバックはsave良いかもしれません。
completeTodo: function (e) {
//...
todo.save({
completed: todoCompleted
});
}
ここでのsave呼び出しは'change:completed'イベントをトリガーするので、それにバインドしてHTMLを調整できます。
removeTodo: function (e) {
//...
}
このdestroy呼び出し"destroy"により、モデルとコレクションでイベントがトリガーされます。
コレクション内のモデルでトリガーされるイベントは、便宜上、コレクションでも直接トリガーされます。これにより、コレクション内の任意のモデルの特定の属性への変更をリッスンできます[...]
"destroy"そのため、コレクションのイベントをリッスンし、それらを使用してTODOを表示から削除できます。そして、モデルを破壊すると、あなたの介入なしにコレクションからモデルを削除する必要があります。
printColl: function () {
this.collection.each(function (todo) {
console.log('ID: '+todo.get('id')+' | CONTENT: '+todo.get('content')+' | COMPLETED: '+todo.get('completed'));
});
}
代わりに、コンソールでコンテンツを開くために少しconsole.log(this.collection.toJSON())クリックする必要がありますが、その方法で何かを見逃すことはありません。
initializeコレクションのすべてのイベントバインディングは、ビューのメソッドで行われます。removeビューを削除する場合は、メモリリークを防ぐために、コレクションからバインドを解除するためにをオーバーライドする必要があります。
remove: function() {
// Call this.collection.off(...) to undo all the bindings from
// `initialize`.
//...
// Then do what the default `remove` does.
this.$el.remove()
}
TODOアイテムごとに個別のビューを使用することもできますが、それは単純なものにはやり過ぎかもしれません。