我不认为在这种情况下我会创建一个单独的“TodoSubItem” - 为什么不HasMany
从 Todo->Todo 创建一个关系,所以一个 Todo 可以有 0..*children
和 0..1 parent
?
这样,您可以重新使用订单逻辑(如果您将其更改为按集合应用),可以根据需要创建更深的嵌套级别(或将其限制到一定深度,如果您也愿意)等等。不过,为了适应这一点,需要更新一些东西——例如,保留一个子视图列表,以便您可以遍历它们以将每个视图标记为已完成,并维护(和更新)每个TodoList
.
无论如何,一个可以帮助您入门的可能解决方案的粗略轮廓,作为与您当前版本的一种差异(抱歉,它完全未经测试,因此可能包含可怕的错误):
//Our basic **Todo** model has `text`, `order`, and `done` attributes.
window.Todo = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'children',
relatedModel: 'Todo',
collectionType: 'TodoList',
reverseRelation: {
key: 'parent',
includeInJSON: 'id'
}
}],
initialize: function() {
if ( !this.get('order') && this.get( 'parent' ) ) {
this.set( { order: this.get( 'parent' ).nextChildIndex() } );
}
},
// Default attributes for a todo item.
defaults: function() {
return { done: false };
},
// Toggle the `done` state of this todo item.
toggle: function() {
this.save({done: !this.get("done")});
}
nextChildIndex: function() {
var children = this.get( 'children' );
return children && children.length || 0;
}
});
// The DOM element for a todo item...
window.TodoView = Backbone.View.extend({
//... is a list tag.
tagName: "li",
// Cache the template function for a single item.
template: _.template($('#item-template').html()),
// The DOM events specific to an item.
events: {
'click': 'toggleChildren',
'keypress input.add-child': 'addChild',
"click .check" : "toggleDone",
"dblclick div.todo-text" : "edit",
"click span.todo-destroy" : "clear",
"keypress .todo-input" : "updateOnEnter"
},
// The TodoView listens for changes to its model, re-rendering.
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
this.model.bind( 'update:children', this.renderChild );
this.model.bind( 'add:children', this.renderChild );
this.el = $( this.el );
this.childViews = {};
},
// Re-render the contents of the todo item.
render: function() {
this.el.html(this.template(this.model.toJSON()));
this.setText();
// Might want to add this to the template of course
this.el.append( '<ul>', { 'class': 'children' } ).append( '<input>', { type: 'text', 'class': 'add-child' } );
_.each( this.get( 'children' ), function( child ) {
this.renderChild( child );
}, this );
return this;
},
addChild: function( text) {
if ( e.keyCode == 13 ) {
var text = this.el.find( 'input.add-child' ).text();
var child = new Todo( { parent: this.model, text: text } );
}
},
renderChild: function( model ) {
var childView = new TodoView( { model: model } );
this.childViews[ model.cid ] = childView;
this.el.find( 'ul.children' ).append( childView.render() );
},
toggleChildren: function() {
$(this.el).find( 'ul.children' ).toggle();
},
// Toggle the `"done"` state of the model.
toggleDone: function() {
this.model.toggle();
_.each( this.childViews, function( child ) {
child.model.toggle();
});
},
clear: function() {
this.model.set( { parent: null } );
this.model.destroy();
}
// And so on...
});