环境
# 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 的模型钩子应该返回一个对象数组。像这样:
注意:缩进只是为了使示例更具可读性。
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'
的显然来自这段代码:
var regions = countries.map(function(country){
return country.get('regions');
});
var areas = regions.then(function(regions){
// regions is not a promise
但是,这应该表明我遇到的真正问题:
问题
我需要countries
下定决心regions
获得areas
. 我一直在检查RSVP.hash
andRSVP.all
函数,阅读官方 API 并观看这个演讲,但是我有点未能创建正确的代码来链接 Promise 并在最终then
修改返回的结果以符合我的期望。
最后的想法
有人告诉我,像这样加载数据可能会导致许多 HTTP 请求,并且可能通过侧载更好地解决这个问题,但是:
- 此时,我使用
FixturesAdapter
,所以 HTTP 请求不是问题 - 我真的很想更好地理解 RSVP 和 Promises
这就是为什么弄清楚这应该如何正确完成对我来说很重要。
编辑 1:应用kingpin2k建议的更改
我为我的示例创建了一个JSBin,其中包含 kingpin2k 的 anwser 建议的更改。
虽然代码有效,但结果是......意想不到的:
- 在
countries
数组中我找到了country
和region
对象。为什么? - 和对象似乎已加载,但区域没有(请参阅 JSBin 中的控制台日志结果)。
country
为什么?region
编辑 2:来自 Edit1 的意外行为的解释。
所以我终于注意到我偏离了 Ember 的正义之路的地方。Kingpin2k 的 anwser 是一个巨大的进步,但它包含一个小错误:
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 = [];
所以..现在我终于正确解决了所有对象(国家、地区、地区)并且可以继续我的工作:)