【问题标题】:Creating nested models in Backbone with Backbone-relational使用 Backbone-relational 在 Backbone 中创建嵌套模型
【发布时间】:2011-08-29 08:04:35
【问题描述】:

我想在我的backbone.js 应用程序中使用backbone-relational 来嵌套模型。

我已经能够按照文档中的示例创建嵌套对象(例如一对多关系)。但是我不明白如何以更新上层对象的方式绑定下层元素。我认为一个工作应用程序将是一个非常有用的教程。

所以我的问题是:如何使用 backbone-relational 扩展 Todos tutorial 以便:

  • 可以为每个项目添加/删除子项目
  • 双击任何子项对其进行编辑(就像最初的 Todo 示例一样)
  • 单击项目隐藏/显示其子项目
  • 子项不单独获取,而只是 Todo 项的数组属性

更新:我有created a jsfiddle for this question。到目前为止,我有:

  • 导入了上面提到的 Todo 示例
  • 创建了一个TodoSubitem 模型和一个TodoSubitemList 集合
  • Todo 模型更改为扩展RelationalModel 而不是ModelHasManyTodoSubitem 的关系
  • 在 html 代码中添加了subitem-template

但我仍然不确定如何:

  • subitems 添加一个输入字段,该字段仅在您单击Todo div 时出现
  • 将子项数据作为Todo 对象的属性,但仍有TodoSubitemView 将DOM 元素绑定到它们(例如<li> 标签)。

【问题讨论】:

    标签: javascript backbone.js backbone-relational


    【解决方案1】:

    我不认为在这种情况下我会创建一个单独的“TodoSubItem” - 为什么不从 Todo->Todo 创建一个 HasMany 关系,所以一个 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...
    });
    

    【讨论】:

    • 谢谢保罗!我将尝试将其合并到我的示例中。
    • 完整的代码示例会很好 - 我无法理解这一点。
    • 除非我弄错了,否则这是行不通的,因为 Backbone-relational 需要定义 relatedModel 才能创建模型之间的关系。
    【解决方案2】:

    我认为您不能在 Backbone-relational 中创建自相关模型(如此处的另一个答案所述)。当我尝试这样做时,我收到一个错误:Backbone-relational 需要先定义 relatedModel,然后才能与它创建关系。

    所以,我修改了主干关系页面上描述的多对多模式:

    https://github.com/PaulUithol/Backbone-relational#many-to-many-relations

    本质上,我正在创建一个链接模型以包含对所引用模型的引用,以便在定义实际模型时,该链接模型可用于 Backbone-relational。

    我发现给这个链接模型与关系中的两个数据模型单独的关系很方便,这样任何一个都可以执行查找关系查找。或者,您可以简单地将第二个模型填充到链接模型中,但是除非您在数据模型中明确添加对链接模型的引用,否则这种关系将是单向的。

    让我们创建一个“Person”模型,其中包含其他“Person”模型的子代。

    Person = Backbone.RelationalModel.extend({
    relations: [
        {
            type: 'HasMany',
            key: 'Children',
            relatedModel: 'FamilyRelation',
            reverseRelation: {
                key: 'Childrenof'
            }
        },
        {
            type: 'HasMany',
            key: 'Parent',
            relatedModel: 'FamilyRelation',
            reverseRelation: {
                key: 'Parentof'
            }
        }
    ]
    });
    

    FamilyRelation 需要在 >before

    // FamilyRelation is link model between two "Person"s 
    // to achieve the Fan/Admiree relation.
    
    FamilyRelation = Backbone.RelationalModel.extend({
    })
    

    如果我们创建两个“Person”:

    KingKong = new Person({name: 'KingKong'});
    SonOfKong = new Person({name: 'SonOfKong'});
    

    然后我们可以创建一个 FamilyRelationship 模型,它是 SonOfKong 的 'parentof',并使用以下代码将其添加到 KingKong 的孩子:

    KingKong.get("children").add({"parentof":SonOfKong});
    

    然后,您可以向 Person 模型添加便利函数,以从 FamilyRelationship 模型中检索嵌套模型,并且实际上不需要再接触 FamilyRelation,除非确保它被适当地保存和检索。

    对于非层次关系(比如“朋友”,而不是“父母/孩子”,您仍然需要这两个关系与链接模型,以便能够从另一个中检索一个,这有点破解,但它有效。

    【讨论】:

      【解决方案3】:

      经过一番折腾,我找到了一种创建真正嵌套模型的方法:

      var theModel = Backbone.RelationalModel.extend({ [...] });
      theModel.prototype.relations.push({
        type: Backbone.HasOne,
        key: 'key',
        relatedModel: theModel
      });
      

      在使用模型时(推送到原型上的关系时)它是可用的,因此一切正常。

      【讨论】:

      • 哇,这么简单。我希望我能在几个小时前找到这个。
      【解决方案4】:

      这篇文章现在已经很老了,但我正在寻找同样的东西,并认为我会分享我得到的解决方案。

      要创建自引用模型,您只需省略 relatedModel。所以是这样的:

      Person = Backbone.RelationalModel.extend({ relations: [{ type: 'HasMany', key: 'Children', }] })

      说明in the docs

      【讨论】:

        猜你喜欢
        • 2017-03-19
        • 2012-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多