我认为我有一个非常简单的问题,很难说出来,因此很难找到解决方案。设置:
当我获取PathCollection
时paths = new PathCollection()
paths.fetch()
显然,Paths被实例化了。但是,我错过了我可以允许Path从属性哈希实例化其子模型的地方。我不能真正使用解析,对吧?基本上我正在寻找模型的入口点,当它实例化并设置属性时。我觉得必须有一些约定。
答案 0 :(得分:25)
所以我写了几个关于使用parse()
和set()
来实例化和填充子模型和子集合(嵌套数据)的答案。但是,我还没有看到一个非常全面的答案,它巩固了我所看到的一些实践。当我写很多东西时,我倾向于絮絮叨叨,所以我可能会稍微离题,但这对于遇到类似问题的人来说可能是有用的。
有几种方法可以做到这一点。使用parse()
是一个。操纵set()
是另一个。在initialize()
中实例化这些是另一个。在Path模型之外完成所有操作(例如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()
可以派上用场。
parse()
的问题是它需要任何json服务器响应,并且可以在将其传递给模型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”。所以我们要确保我们的{ {1}}适当地处理此事。
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);
}
。如果我们有一个属性但它不是一个集合,我们实例化它。在将子数据的JSON数组传递给原型reset()
之前,确保正确地将其转换为集合非常重要。 Backbone,不会将JSON数组解释为集合,您只能获得一个直接数组。
所以在坚果壳中,你有很多关于如何去做的选择。同样,目前我赞成混合使用set()
当我知道某些内容将始终具有这些子模型/集合时,initialize()
当情况仅调用parse()
调用上可能的嵌套数据时。
关于你的那个问题......(哦,是的,有一个问题)
您可以允许Path以各种方式从哈希实例化子模型。我刚给你4.如果你想要,你可以使用解析,如果你知道你将成为fetch()
路径模型或者甚至是pathCollection ...... fetch()
是否有约定?也许不是。我喜欢使用各种方式的组合,这取决于我认为我将使用模型/集合的上下文。
我很乐意讨论其中的一些做法以及它们的好坏。它们只是我在Stack上遇到的许多解决方案,并融入了我自己的工作习惯中,它们似乎对我来说很好。 : - )
给自己喝杯咖啡,拍拍背面,这是一个很长的阅读时间。