6

我想我有一个非常简单的问题,很难说,因此很难找到解决方案。设置:

  • PathCollection 是一个 Backbone.Collection 的路径
  • Path 是一个 Backbone.Model,它包含 NodeCollection(它是一个 Backbone.Collection)和 EdgeCollection(它是一个 Backbone.Collection)。

当我获取 PathCollection

paths = new PathCollection()
paths.fetch()

显然,路径被实例化。但是,我错过了允许 Path 从属性哈希实例化其子模型的地方。我真的不能使用解析,对吧?基本上,当模型实例化并设置属性时,我正在寻找模型的入口点。我觉得必须有一些约定。

4

1 回答 1

25

所以我写了几个关于使用parse()set()实例化和填充子模型和子集合(嵌套数据)的答案。但是,我还没有看到一个真正全面的答案来整合我所看到的许多实践中的一些。当我写很多东西时,我倾向于漫不经心,所以我可能会离题一点,但这可能对遇到类似问题的人有用。

有几种方法可以做到这一点。使用parse()是一个。操纵set()是另一回事。在你的实例化这些initialize()是另一个。在路径模型之外做这一切是另一个(例如path = new Path(); path.nodes = new NodeCollection();等)

第二个考虑是这个。您希望节点和边集合成为模型属性吗?还是模型属性?

哦,这么多选择。很多自由,但有时(令我们沮丧的是)这使得确定“正确的方式”变得更加困难。

由于这种情况经常出现,因此我将发表一篇长文并一一介绍。所以请耐心等待我继续更新这个答案。

在模型之外进行 - 简单直接

当您只需要在特定模型或集合上添加嵌套模型和集合时,这通常是一种简单的方法。

path = new PathModel();

path.nodes = new NodeCollection();
path.edge = new EdgeCollection();

// Continue to set up the nested data URL, etc.

这是最简单的方法,当您处理不需要定义的一次性模型和集合时效果很好。尽管您可以在对它进行任何操作之前通过构造该对象的某种方法(例如视图方法)轻松生成这些模型。

在每个模型中使用initialize()子模型/集合

如果您知道某个模型的每个实例总是有一个子模型或子集合,那么最简单的设置方法就是使用该initialize()函数。

例如,以您的 Path 模型为例:

Path = Backbone.Model.extend({
    initialize: function() {
        this.nodes = new NodeCollection();
        this.paths = new PathCollection();

        // Maybe assign a proper url in relation to this Path model
        // You might even set up a change:id listener to set the url when this
        // model gets an id, assuming it doesn't have one at start.
        this.nodes.url = this.id ? 'path/' + this.id + '/nodes' : undefined;
        this.paths.url = this.id ? 'path/' + this.id + '/paths' : undefined;
    }
});

现在您的子集合可以像这样被获取path.nodes.fetch(),它会路由到正确的 URL。十分简单。

用于parse()实例化和设置子数据

如果你不想假设每个模型都有一个节点和边缘集合,也许它会变得有点棘手。fetch()也许只有在发回此类数据时才需要嵌套模型/集合。这就是使用parse()可以派上用场的情况。

问题是它需要任何 json 服务器响应,并且可以在将其传递给模型函数parse()之前正确命名空间和处理它。set()因此,我们可以检查是否包含模型或集合原始数据并在将响应减少到父模型属性之前对其进行处理。

例如,也许从我们的服务器我们得到这个响应:

// Path model JSON example with nested collections JSON arrays
{
    'name':'orange site',
    'url':'orange.com',
    'nodes':[
        {'id':'1', 'nodeColor':'red'},
        {'id':'2', 'nodeColor':'white'},
        {'id':'3', 'nodeColor':'blue'}
    ],
    'edge':[
        {'id':'1', 'location':'north'},
        {'id':'1', 'location':'south'},
        {'id':'1', 'location':'east'}
    ]
}

使用默认的parse()Backbone 将吞噬它,并为您的路径模型属性“nodes”和“edge”分配一个数据数组(而不是集合)。所以我们要确保我们能parse()适当地处理这个问题。

parse: function(response) {

    // Check if response includes some nested collection data... our case 'nodes'
    if (_.has(response, 'nodes')){

         // Check if this model has a property called nodes
        if (!_.has(this, 'nodes')) {  // It does not...
            // So instantiate a collection and pass in raw data
            this.nodes = new NodeCollection(response.nodes);
        } else {
            // It does, so just reset the collection
            this.nodes.reset(response.nodes);
        }

        // Assuming the fetch gets this model id
        this.nodes.url = 'path/' + response.id + '/nodes';  // Set model relative URL

        // Delete the nodes so it doesn't clutter our model attributes
        delete response.nodes;
    }

    // Same for edge...

    return response;
}

你也可以使用自定义set()的来处理你的子数据。经过多次来回哪个更好,操纵set()或这样做后,parse()我决定我喜欢使用parse()更多。但我愿意接受其他人对此的看法。

用于set()处理您的子数据

虽然parse()依赖于获取数据或使用选项将数据传递到集合中,但parse:true有些人发现更改set()函数更可取。同样,我不确定是否有正确的选择,但这就是它的工作方式。

set: function(attributes, options) {
    // If we pass in nodes collection JSON array and this model has a nodes attribute
    // Assume we already set it as a collection
    if (_.has(attributes, 'nodes') && this.get("nodes")) {
        this.get('nodes').reset(attributes.nodes);
        delete attributes.nodes;
    } else if (_.has(attributes, 'nodes') && !this.get('nodes')) {
        this.set('nodes', new NodeCollection(attributes.nodes));
        delete attributes.nodes;
    }

    return Backbone.Model.prototype.set.call(this, attributes, options);
}

因此,如果我们已经有一个属性并且它是一个集合,我们就使用reset()它。如果我们有一个属性但它不是一个集合,我们就实例化它。在将子数据的 JSON 数组传递给原型之前,确保将其正确转换为集合非常重要set()。Backbone,不会将 JSON 数组解释为一个集合,您只会得到一个直接的数组。

所以简而言之,你有很多关于如何去做的选择。initialize()同样,当我知道某些东西总是有那些子模型/集合并且parse()情况只需要调用可能的嵌套数据时,我现在更喜欢混合使用fetch()

关于你的那个问题......(哦,是的,有一个问题)

您可以允许 Path 以多种方式从散列中实例化子模型。我刚刚给了你 4。如果你愿意,你可以使用 parse,如果你知道你将成为fetch()路径模型,或者甚至可能是 pathCollection...pathCollection.fetch({parse:true})有约定吗?也许也许不是。我喜欢根据我认为我将使用模型/集合的上下文来使用多种方式。

我非常愿意讨论其中的一些做法,以及它们是好是坏。它们只是我在 Stack 上遇到的许多解决方案,并融入了我自己的工作习惯,它们似乎对我来说效果很好。:-)

给自己喝杯咖啡,拍拍背,那是一篇长篇大论。

于 2012-09-10T14:42:55.023 回答