Ember.js &Ember 数据:如何在父子不存在时创建具有 hasMany 关系的记录 [英] Ember.js & Ember Data: How to create record with hasMany relationship when the parent and child don't exist yet
问题描述
就像我使用 ember-data 所做的大多数事情一样,简单的测试用例非常简单,但是由于我缺乏理解(我认为文档),任何真实"的东西很快就会崩溃.在最简单的情况下,创建新记录并将其保存到我的 REST API 没有问题.我将使用流行的水果篮示例,并从保存一片新的 fruit
开始.
As seems to be the case with most everything I do with ember-data the simple test case is very simple but anything "real" falls apart quickly due to my lack of understanding (and I think documentation). In the simplest case I have no problem creating a new record and saving it to my REST API. I'll use the prevalent fruit basket example and start with just saving a new piece of fruit
.
// Here is our fruit model
module.exports = App.Fruit= DS.Model.extend({
name: attr('string'),
color: attr('string')
});
// This is easy to create and save
var fruit = this.store.createRecord('fruit', {
name: 'apple',
color: 'red'
});
fruit.save().then(successCallback, errorCallback);
好的,我这里没有问题.从概念上讲是有道理的,代码很干净,事情看起来很好!现在假设我们有一个 basket
模型,并且 basket
模型可以容纳很多 fruit
.此外,用户可以通过 UI 一步创建新的 fruit
并将其添加到尚未创建的新 basket
.
Okay, I have no problem here. Conceptually makes sense, the code is clean, and things look peachy! Now suppose that we have a basket
model and that basket
model can hold lots of fruit
. Furthermore, the user can create new fruit
and add it to a new basket
that has not been created yet in one step from the UI.
这里是新模型:
module.exports = App.Fruit= DS.Model.extend({
name: attr('string'),
color: attr('string')
basket: DS.belongsTo('basket')
});
module.exports = App.Basket = DS.Model.extend({
fruits: DS.hasMany('fruit', {async:true})
});
鉴于上述情况,假设用户输入了两个当前不存在于商店或后端 REST API 中的新水果,将它们添加到他的新篮子(也不存在)中,然后点击保存".
Given the above, lets say the user inputs in two new fruits that don't currently exist in the store or in the backend REST API, adds them to his new basket (which also doesn't exist yet) and then hits 'save'.
您如何使用 Ember Data 创建此记录?我希望你能做这样的事情:
How do you create this record using Ember Data? I was hoping you could do something like this:
var newFruits = Ember.A();
newFruits.pushObject(this.store.createRecord('fruit', {
name: 'orange',
color: 'orange'
}));
newFruits.pushObject(this.store.createRecord('fruit', {
name: 'banana',
color: 'yellow'
}));
var basket = this.store.createRecord('basket', {
fruits: newFruits
});
basket.save().then(successCallback, errorCallback);
不幸的是,这似乎不起作用.当我在 Chrome 检查器中查看 POST 请求数据时,篮子的 fruits
属性始终为空.基本上,请求最终看起来是这样的:
Unfortunately, this doesn't seem to work. When I look at the POST request data in Chrome inspector the fruits
property of basket is always empty. Basically, the request ends up looking this:
{
basket: {
fruits: []
}
}
我是否使用正确的模式来创建篮子"记录?我希望我不必在保存篮子之前先保存每块水果,因为当 1 个就足够时发送 3 个 POST 请求似乎很浪费.另外,当用户想要创建 10 个水果时会发生什么?那将是 11 个 POST 请求.
Am I using the correct pattern to create the 'basket' record? I'm hoping I don't have to end up saving each piece of fruit first before saving the basket as it seems wasteful to send 3 POST requests when 1 would suffice. Additionally, what happens when the user wants to create 10 pieces of fruit? That would be 11 POST Requests.
是否有完成上述任务的标准做法?本质上,您如何创建一个具有 hasMany 关系(篮子)的新记录,其中 hasMany 关系中的记录也是新的(结果).
Is there a standard practice for accomplishing the above? Essentially, how do you create a new record with a hasMany relationship (basket) where the records in the hasMany relationship are also new (the fruit).
谢谢!
以下是我使用的版本:
-------------------------------
Ember : 1.3.2+pre.25108e91
Ember Data : 1.0.0-beta.7+canary.238bb5ce
Handlebars : 1.3.0
jQuery : 2.0.3
-------------------------------
推荐答案
我只是自己解决这个问题,但我就是这样做的,而且效果很好……
I'm just figuring this out myself, but I just did it this way and it works…
对于初学者来说,当您 this.store.createRecord('basket', {})
时,它的 fruits
属性应该带有一个空数组.所以,这样做:
For starters, when you this.store.createRecord('basket', {})
it should come with an empty array for its fruits
property. So, do this:
var basket = this.store.createRecord('basket', {});
basket.get('fruits').addObject(this.store.createRecord('fruit', {
name: 'orange',
color: 'orange'
}));
basket.get('fruits').addObject(this.store.createRecord('fruit', {
name: 'banana',
color: 'yellow'
}));
顺便说一下,空的 fruits
数组实际上是一个 DS.PromiseArray
(它可能会立即解析为一个 DS.ManyArray
?),一个Ember.Array
的更具体的子类,所以这可能会有所作为.
Incidentally, the empty fruits
array is actually a DS.PromiseArray
(which may instantly resolve to a DS.ManyArray
?), a more specific subclass of Ember.Array
, so that could make a difference.
然而,我发现 fruits
属性根本不会被序列化——不过我使用的是 JsonApiAdapter,所以 可能 是原因.或者这可能是 hasMany
子对象尚未持久化时的预期行为.无论如何,我只是在我的控制器的 actions.save 方法中做了这样的事情:
However, what I found was that the fruits
property wouldn't get serialized at all--I'm using the JsonApiAdapter though, so that could be the reason. Or it might be that this is the expected behavior when the hasMany
child objects haven't been persisted yet. Anyway, I just did something like this in my controller's actions.save method:
basket.save().then(function(){
var promises = Ember.A();
basket.get('fruits').forEach(function(item){
promises.push(item.save());
});
Ember.RSVP.Promise.all(promises).then(function(resolvedPromises){
alert('All saved!');
});
});
不知何故,神奇的是,当所有这些都完成后,我最终得到了一个保存的篮子",带有生成的 id
,并保存了带有生成的 id
的水果"s.而且,所有水果的 belongsTo
属性都指向篮子,篮子的 hasMany
包含所有水果……尽管事实上,当我 save()
d 篮子,我后端的响应为我的 fruits
属性返回了一个空数组,我确信这会搞砸,但不知何故没有.
Somehow, magically, when all that was done, I ended up with a saved "basket", with a generated id
, and saved "fruits" with generated id
s. And, all the fruits' belongsTo
properties pointed back at the basket, and the basket's hasMany
contained all the fruits…despite the fact that, when I save()
d the basket, the response from my backend returned an empty array for my fruits
property, which I was sure would mess things up but somehow didn't.
当然,这些不是我的实际模型,但相似度非常接近.我正在使用 Ember Data 1.0.0-beta5 和 Ember 1.3.1.我没有使用库存的 REST 适配器,但 JsonApiAdapter 是一个非常薄的外壳——所以试试这个方法,看看它是否有效!
Of course, these weren't my actual models, but the parallel was very close. I'm on Ember Data 1.0.0-beta5 and Ember 1.3.1. I'm not using the stock REST adapter, but the JsonApiAdapter is a pretty thin shell over it--so try this approach and see if it works!
我在此处打开了一个类似的问题 解决了我在使用上述代码更新现有记录(而不是创建新记录)时遇到的问题.该解决方案更通用、更详细,但也更复杂.
I've opened a similar question here that addresses a problem I encountered using the above code to also update existing records (as opposed to creating new ones). That solution is more general and detailed but more complex.
此外,我遇到的另一个潜在问题是,当(在本例中)创建 basket
时,fruits
属性(这是一个 DS.PromiseArray
) 可能还没有完全准备好.所以你可能需要这样做:
Also, another potential gotcha I ran into is that when (in this example) the basket
is created, the fruits
property (which is a DS.PromiseArray
) may not be fully ready yet. So you may need to do it like this instead:
var basket = this.store.createRecord('basket', {});
basket.get('fruits').then(function(){
// Now the fruits array is available
basket.get('fruits').addObject(this.store.createRecord('fruit', { /* ... */ }));
});
这篇关于Ember.js &Ember 数据:如何在父子不存在时创建具有 hasMany 关系的记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!