環境
# Ember : 1.4.0
# Ember Data : 1.0.0-beta.7+canary.b45e23ba
モデル
質問を理解しやすくするために、ユースケースを単純化しました。Country
、Region
およびの 3 つのモデルがあるとし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')
予想された結果
ルートのモデル フックは、オブジェクトの配列を返す必要があります。このような:
注:インデントは、例を読みやすくするためだけのものです。
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 を読み、このトークを見てきましたが、promise をチェーンするための正しいコードを作成できず、最終的then
に返された結果を期待どおりに変更することができませんでした。
最終的な考え
このようなデータをロードすると、多くの HTTP リクエストが発生する可能性があり、おそらくこれはサイドローディングによってより適切に解決されると言われていますが、次のようになります。
- 現時点では を使用
FixturesAdapter
しているため、HTTP リクエストは問題になりません - RSVP と Promises をもっとよく理解したい
そのため、これを正しく行う方法を理解することが重要です。
編集 1: kingpin2kによって提案された変更の適用
kingpin2k の anwser によって提案された変更を加えて、私の例のJSBinを作成しました。
コードは機能しますが、結果は...予期しないものです:
countries
配列でcountry
とregion
オブジェクトの両方を見つけました。なんで?country
およびオブジェクトはロードされているように見えますが、エリアはロードされていませんregion
(JSBin のコンソール ログの結果を参照してください)。
編集 2: Edit1 からの予期しない動作の説明。
ということで、エンバーの正道から外れていたところにようやく気がつきました。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 = [];
だから..今、私は最終的にすべてのオブジェクト(国、地域、地域)を正しく解決し、仕事を続けることができます:)