使用 Backbone 关系在 Backbone 中创建嵌套模型

发布于 2024-12-02 06:43:47 字数 1262 浏览 0 评论 0 原文

我想使用 backbone-relational 在我的 backbone.js 应用程序。

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

所以我的问题是:如何使用 Todos 教程 >backbone-relational 这样:

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

更新:我已经创建了一个jsfiddle 对于这个问题。到目前为止,我已经:

  • 导入了上面提到的 Todo 示例
  • 创建了一个 TodoSubitem 模型和一个 TodoSubitemList 集合
  • 更改了 Todo 模型以扩展 RelationalModel 而不是 Model,与 TodoSubitem 具有 HasMany 关系
  • 在html 代码

但我仍然​​不确定如何:

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

I would like to use backbone-relational to have nested models in my backbone.js application.

I have been able to follow the examples in the documentation to create nested objects (e.g. one-to-many relations). However I don't understand how to bind the lower level elements in a way that will update the upper level objects. I think a working application would be a very helpful tutorial.

So my question is: How do I extend the Todos tutorial using backbone-relational so that:

  • one can add/remove subitems for each item
  • double clicking on any subitem edits it (just like the original Todo example)
  • clicking on an item hides/reveals its subitems
  • subitems are not fetched separately but are simply an array attribute of Todo items

Update: I have created a jsfiddle for this question. So far I have:

  • Imported the Todo example mentioned above
  • Created a TodoSubitem model and a TodoSubitemList collection
  • Altered the Todo model to extend RelationalModel instead of Model, with a HasMany relation to TodoSubitem
  • Added a subitem-template in the html code

But I'm still not sure how to:

  • add an input field for subitems that appears only when you click a Todo div
  • have subitem data as an attribute of Todo objects, but still have TodoSubitemView bind DOM elements to them (e.g. <li> tags).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

野侃 2024-12-09 06:43:47

在这种情况下,我认为我不会创建一个单独的“TodoSubItem” - 为什么不从 Todo->Todo 创建一个 HasMany 关系,这样一个 Todo 可以有 0..* 孩子,和0..1 父母

这样,您可以重复使用顺序逻辑(如果将其更改为适用于每个集合),可以根据需要创建更深的嵌套级别(或者如果您也愿意,则将其限制在一定深度)等。不过,为了适应这一点,需要更新一些内容 - 例如,保留子视图列表,以便您可以循环遍历它们以将每个视图标记为已完成,并维护(并更新)每个 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...
});

I don't think I'd create a separate 'TodoSubItem' in this case - why not create a HasMany relation from Todo->Todo, so a Todo can have 0..* children, and 0..1 parent?

This way, you can re-use the order logic (if you change it to apply per collection), can create deeper nesting levels as desired (or limit that to a certain depth, if you want as well), etc. A number of things will need to be updated though, to accomodate this - for example, keep a list of child views so you can loop over them to mark each as done, and maintaining (and updating from) an ordering per TodoList.

Anyway, a rough outline of a possible solution to get you started, as a sort of diff with your current version (sorry, it's completely untested and could thus contain horrible mistakes):

//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...
});
遇见了你 2024-12-09 06:43:47

我不认为你可以在骨干关系中建立自相关模型(如此处另一个答案所述)。当我尝试此操作时,出现错误: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< Person 是,因此 Backbone-relational 可以创建链接,因此这位于代码中的 Person 模型定义之前:

// 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,除了使确保它被正确保存和检索。

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

I don't think you can make self-relating models in Backbone-relational (as described an the other answer here). When I have tried this, I get an error: Backbone-relational needs the relatedModel to be defined before it can create relationships with it.

So, I've modified the many-to-many pattern described on the backbone-relational page:

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

In essence, I am creating a linking model to contain references to the model being referred to, so that this link model can be available to Backbone-relational when it is defining the actual model.

I find it convenient to give this link model a separate relationship with both data models in the relationship, so that either can perform look relational look ups. Alternately, you could simply stuff the second model inside the link model, but then the relationship would be one directional unless you explicitly add your own references to the link model in the data model.

Let us create a 'Person' model that has children who are other 'Person' models.

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

FamilyRelation needs to be defined >before< Person is, so Backbone-relational can create the links, so this goes before the Person model definition in your code:

// FamilyRelation is link model between two "Person"s 
// to achieve the Fan/Admiree relation.

FamilyRelation = Backbone.RelationalModel.extend({
})

If we create two "Person"s:

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

Then we can create a FamilyRelationship model that is the 'parentof' SonOfKong, and add it to KingKong's children with this line:

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

You can then add convenience functions to the Person model, to retrieve the nested models from the FamilyRelationship model, and don't really need to touch FamilyRelation any more, except to make sure it's being saved and retrieved appropriately.

For non-hierarchical relationships (say 'Friend', rather than 'Parent/Child', you still need these two relationships with the linking model in order to be able to retrieve one from the other, which is a bit of a hack, but it works.

和我恋爱吧 2024-12-09 06:43:47

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

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

在使用模型的地方(当推送到原型上的关系时)它是可用的,从而使一切正常工作。

After some fiddling I have found a way to create a true nested model:

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

At the point where the model is used (when pushing to the relations on the prototype) it is available, thus making everything work.

一身骄傲 2024-12-09 06:43:47

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

要创建自引用模型,您只需省略 latedModel 即可。所以像这样:


人 = Backbone.RelationalModel.extend({
关系:[{
类型:'HasMany',
键:“孩子”,
}]
})

文档中对此进行了解释

this post is pretty old by now, but I was searching for the same thing and thought I would share the solution I got.

To create a self-referencing model you simply omit relatedModel. So something like this:


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

It is explained in the docs

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文