如何使用 EmberData 在 EmberJS 中返回由嵌套模型组成的承诺? [英] How to return a promise composed of nested models in EmberJS with EmberData?
问题描述
# Ember : 1.4.0
# Ember Data : 1.0.0-beta.7+canary.b45e23ba
型号
我简化了我的用例,使问题更容易理解和回答.假设我们有 3 个模型:Country
、Region
和 Area
:
Country:
- id: DS.attr('number')
- name: DS.attr('string')
- regions: DS.hasMany('region')
Region:
- id: DS.attr('number')
- name: DS.attr('string')
- country: DS.belongsTo('country')
- areas: DS.hasMany('area')
Area:
- id: DS.attr('number')
- name: DS.attr('string')
- region: DS.belongsTo('region')
预期结果
Route 的模型钩子应该返回一个对象数组.像这样:
Expected results
The Route's model hook should return an array of objects. Like this:
注意:缩进只是使示例更具可读性.
Note: The indentations are only to make the example more readable.
Country I
Region A
Area 1
Area 2
Region B
Area 3
Country II
Region C
Area 4
Country III
Region D
Area 5
目前的方法
App.MyRoute = Ember.Route.extend({
model: function() {
return this.store.find('country').then(function(countries){
// promise all counties
// map resolved countires into an array of promises for owned regions
var regions = countries.map(function(country){
return country.get('regions');
});
// map resolved regions into an array of promises for owned areas
var areas = regions.then(function(regions){
return regions.map(function(region){
return region.get('areas');
});
});
// do not return until ALL promises are resolved
return Ember.RSVP.all(countries, regions, areas).then(function(data){
// here somehow transform the data into expected output format and return it
});
});
}
)};
错误
我收到 Error while loading route: TypeError: Object [object Array] has no method 'then'
这显然来自这个代码:
Error
I'm getting Error while loading route: TypeError: Object [object Array] has no method 'then'
which obviously comes from this code:
var regions = countries.map(function(country){
return country.get('regions');
});
var areas = regions.then(function(regions){
// regions is not a promise
然而,这应该表明我遇到的真正问题:
However this should show the real problem I have:
我需要解析 countries
来获取 regions
,然后我需要获取 areas
.我一直在检查 RSVP.hash
和 RSVP.all
函数,阅读官方 API 并观看 这个谈话,但是我有点未能创建正确的代码来链接承诺,并在最终 then
修改返回的结果以匹配我的期望.
I need countries
resolved to get the regions
, which in turn I need to get the areas
. I've been checking the RSVP.hash
and RSVP.all
functions, reading the official API and watching this talk, however I somewhat fail to create the correct code to chain promises and in the final then
modify the returned result to match my expectations.
有人告诉我,像这样加载数据可能会导致许多 HTTP 请求,可能通过旁加载可以更好地解决这个问题,但是:
I have been told that loading data like this may cause many HTTP requests and probably this would be solved better by sideloading, but:
- 此时,我使用
FixturesAdapter
,所以 HTTP 请求不是问题 - 我真的很想更好地理解 RSVP 和 Promises
- at this moment, I use
FixturesAdapter
, so HTTP requests are not an issue - I really want to understand RSVP and Promises better
这就是为什么弄清楚应该如何正确完成这件事对我来说很重要.
Thats why it is important to me to figure out how this should be done correctly.
我为我的示例创建了一个 JSBin由 kingpin2k 的 anwser 推荐.
I've created a JSBin for my example with changes suggested by kingpin2k's anwser.
虽然代码有效,但结果...出乎意料:
While the code works, the results are... unexpected:
- 在
countries
数组中,我找到了country
和region
对象.为什么? country
和region
对象似乎已加载,但区域未加载(请参阅 JSBin 中的控制台日志结果).. 为什么?强>
- in the
countries
array I found bothcountry
andregion
objects. Why? - the
country
andregion
objects seem to be loaded, but area's don't (see console log results in the JSBin).. Why?
所以我终于注意到我在什么地方偏离了 Ember 的正道.Kingpin2k 的 anwser 向前迈进了一大步,但它包含一个小错误:
So I've finally noticed where I went astray from the righteous path of Ember. Kingpin2k's anwser was a huge step forward, but it contains a little error:
return this.store.find('country').then(function(countries){
// this does not return an array of regions, but an array of region SETs
var regionPromises = countries.getEach('regions');
// wait for regions to resolve to get the areas
return Ember.RSVP.all(regionPromises).then(function(regions){
// thats why here the variable shouldn't be called "regions"
// but "regionSets" to clearly indicate what it holds
// for this example i'll just reassign it to new var name
var regionSets = regions;
// now compare these two lines (old code commented out)
//var areaPromises = regions.getEach('areas');
var areaPromises = regionSets.getEach('areas');
// since regionSet does not have a property "areas" it
// won't return a promise or ever resolve (it will be undefined)
// the correct approach would be reduceing the array of sets
// an array of regions
var regionsArray = regionSets.reduce(function(sum, val){
// since val is a "Ember.Set" object, we must use it's "toArray()" method
// to get an array of contents and then push it to the resulting array
return sum.pushObjects(val.toArray());
}, []);
// NOW we can get "areas"
var realAreaPromises = regionsArray.getEach('areas');
// and now we can use Ember.RSVP to wait for them to resolve
return Ember.RSVP.all(realAreaPromises).then(function(areaSets){
// note: here again, we don't get an array of areas
// we get an array of area sets - each set for the corresponding region
var results = [];
所以..现在我终于正确解析了所有对象(国家、地区、地区)并且可以继续我的工作:)
So.. now I've finally got all the objects correctly resolved (countries, regions, areas) and can continue my work :)
推荐答案
诀窍是您需要先解决某些承诺,然后才能访问这些记录的属性.Ember.RSVP.all
接受一组承诺.Ember.RSVP.hash
获取 promise 的哈希值.不幸的是,在之前的承诺得到解决之前,你无法构建你的承诺(唉,你不知道要获得哪个 regions
直到 countries
已解析,并且在解析 regions
之前您不知道要获取哪些 areas
).在这种情况下,您确实有一系列要获取的承诺(尽管每个级别的承诺数组).Ember 知道要等到最深的承诺得到解决,并使用该值作为模型.
The trick is you need to resolve certain promises before you can access the properties on those records. Ember.RSVP.all
takes an Array of promises. Ember.RSVP.hash
takes a hash of promises. Unfortunately you're in the situation where you can't construct your promises until the previous promises have resolved (a la, you don't know which regions
to get until the countries
are resolved, and you don't know which areas
to get until the regions
are resolved). That being the case you really have a serial set of promises to fetch (albeit arrays of promises at each level). Ember knows to wait until the deepest promise has resolved and to use that value as the model.
现在我们需要假设 regions
和 area
是异步的,如果它们不是,你告诉 Ember Data 信息将包含在请求中country
,或者在带有 region
的请求中,这些集合不会是承诺,所以我在下面包含的代码将不起作用.
Now we need to pretend that regions
and area
are async, if they aren't, you're telling Ember Data the information will be included in the request with country
, or in the request with region
and those collections won't be promises so the code I've included below wouldn't work.
regions: DS.hasMany('region', {async: true})
areas: DS.hasMany('area', {async: true})
App.IndexRoute = Ember.Route.extend({
controllerName: 'application',
model: function() {
return this.store.find('country').then(function(countries){
// get each country promises
var regionCollectionPromises = countries.getEach('regions');
// wait for regions to resolve to get the areas
return Ember.RSVP.all(regionCollectionPromises).then(function(regionCollections){
var regions = regionCollections.reduce(function(sum, val){
return sum.pushObjects(val.toArray());
}, []);
var areaCollectionPromises = regions.getEach('areas');
//wait on the areas to resolve
return Ember.RSVP.all(areaCollectionPromises).then(function(areaCollections){
// yay, we have countries, regions, and areas resolved
return countries;
});
});
});
}
});
说了这么多,因为看起来你正在使用 Ember Data,我只需要返回 this.store.find('country')
并让 Ember Data 在使用时获取数据...这个模板可以在没有所有承诺代码的情况下工作,并且会随着 Ember Data 自己履行承诺而填充(它会在看到您尝试使用数据时请求数据,很好的延迟加载).
All this being said, since it appears you're using Ember Data, I'd just return this.store.find('country')
and let Ember Data fetch the data when it's used... This template would work without all of that promise code, and would populate as Ember Data fulfill's the promises on its own (it will request the data once it sees you've attempted to use the data, good ol' lazy loading).
{{#each country in model}}
Country: {{country.name}}
{{#each region in country.regions}}
Region: {{region.name}}
{{#each area in region.areas}}
Area: {{area.name}}
{{/each}}
{{/each}}
{{/each}}
这篇关于如何使用 EmberData 在 EmberJS 中返回由嵌套模型组成的承诺?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!