环保环境
# 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')
预期成绩
路线的模型 Hook 应返回一个对象数组。像这样:
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
和RSVP.all
函数,阅读官方API并观看this talk,但是我有点无法创建正确的代码来链接 promise ,最终then
修改返回的结果以符合我的期望。最后的想法
有人告诉我,像这样加载数据可能会导致许多HTTP请求,并且可能可以通过侧面加载来更好地解决,但是:
FixturesAdapter
,因此HTTP请求不是这就是为什么对我来说很重要的一点是弄清楚应该如何正确地做到这一点。
编辑1:应用kingpin2k建议的更改
我为我的示例创建了一个JSBin,并带有kingpin2k的anwser建议的更改。
当代码运行时,结果是... 意外的:
在
countries
数组中的country
和region
对象。 为什么? country
和region
对象,但未加载该区域(请参见JSBin中的控制台日志结果)。为什么? 编辑2:编辑1中的意外行为的说明。
所以我终于注意到我从Ember的正义之路误入歧途。 Kingpin2k的解决方案向前迈出了一大步,但其中包含一个小错误:
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 = [];
所以..现在我终于正确解析了所有对象(国家,地区,区域),可以继续我的工作了:)
编辑3:此JSBin中的工作解决方案!
最佳答案
诀窍是您需要先解析某些 promise ,然后才能访问这些记录上的属性。 Ember.RSVP.all
接受一个 promise 数组。 Ember.RSVP.hash
采用promise的哈希值。不幸的是,您处于无法解决之前的 promise 的情况下构造 promise 的情况(哦,您不知道要解决的regions
,直到解决了countries
的情况,并且您也不知道要获取的areas
的情况直到regions
解析)。在这种情况下,您确实有一系列要获取的 promise (尽管每个级别都有 promise 数组)。 Ember 知道等到最深的 promise 得到解决,并使用该值作为模型。
现在我们需要假装regions
和area
是异步的,如果不同步,您就是在告诉Ember Data该信息将包含在country
的请求中,或者包含在region
的请求中,而这些集合将不会得到保证我在下面包含的代码不起作用。
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在使用时获取数据...此模板无需所有 promise 代码即可工作,并将填充为Ember数据本身实现了 promise (一旦看到您尝试使用数据,它就会请求数据,良好的延迟加载)。{{#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}}
关于javascript - 如何在Ember JS中使用Ember Data返回由嵌套模型组成的Promise?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23206883/