Enviroment
# Ember : 1.4.0
# Ember Data : 1.0.0-beta.7+canary.b45e23ba
Modell
Ich habe meine Anwendungsfall vereinfacht die Frage leichter zu verstehen und anwser zu machen. Nehmen wir an, wir haben 3 Modelle: Country
, Region
und Area
:Wie kann man ein Versprechen bestehend aus verschachtelten Modellen in EmberJS mit EmberData zurückgeben?
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')
Erwartete Ergebnisse
Die Modell Haken Route sollte ein Array von Objekten zurück. Wie folgt aus:
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
Aktuelle Ansatz
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
});
});
}
)};
Fehler
ich Error while loading route: TypeError: Object [object Array] has no method 'then'
bekommen, die offensichtlich von diesem Code kommt:
var regions = countries.map(function(country){
return country.get('regions');
});
var areas = regions.then(function(regions){
// regions is not a promise
Dies sollte jedoch zeigen t er echtes Problem habe ich:
Das Problem
Ich brauche countries
beschlossen, den regions
zu bekommen, was wiederum ich die areas
bekommen müssen. Ich habe die RSVP.hash
und RSVP.all
Funktionen überprüft, die offizielle API gelesen und this talk geguckt, jedoch schaffe ich es irgendwie nicht, den richtigen Code für die Kettenversprechen zu erstellen und im letzten then
das zurückgegebene Ergebnis so zu modifizieren, dass es meinen Erwartungen entspricht.
Abschließende Gedanken
Ich habe gesagt, dass Laden von Daten wie diese Anfragen vielen HTTP verursachen und wahrscheinlich würde dies durch sideloading besser gelöst werden, aber:
- in diesem Moment, ich benutze
FixturesAdapter
, so HTTP-Anforderungen sind kein Problem - ich möchte wirklich RSVP verstehen und verspricht bessere
das ist, warum es impo ist mir, um herauszufinden, wie dies richtig gemacht werden sollte.
Edit 1: Anwendung von Änderungen vorgeschlagen von kingpin2k
Ich habe ein JSBin für mein Beispiel mit Änderungen von kingpin2k des anwser vorgeschlagen erstellt.
Während der Code funktioniert, sind die Ergebnisse ... unerwartete:
- im
countries
Array fand ich beidecountry
undregion
Objekte. Warum? - die
country
undregion
Objekte scheinen geladen zu werden, aber die Bereiche nicht (siehe Konsolenprotokoll führt zu JSBin). Warum?
Edit 2: Erklärung des unerwarteten Verhaltens von Edit1.
So habe ich endlich bemerkt, wo ich vom rechten Weg von Ember abirrte. Kingpin2k des anwser war ein großer Schritt nach vorn, aber es enthält einen kleinen Fehler:
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 .. jetzt habe ich endlich alle Objekte korrekt aufgelöst (Länder, Regionen, Gebiete) und kann meine Arbeit fortsetzen :)
Hallo, danke für die Antwort, +1, wie dies hilfreich ist. Allerdings kann ich (noch) nicht akzeptiert werden, da die Ergebnisse immer noch nicht wie erwartet sind. Ich habe meine Frage aktualisiert und ein JSBin-Beispiel eingefügt. Wenn Sie so nett wären, es sich anzusehen, tun Sie es bitte. Prost – loostro
Hallo, ich habe gefunden, wo das Problem war. Ihr Verstärker hat einen nicht offensichtlichen logischen Fehler. Sie haben mir geholfen, mit Ihrer Anwers viel, also wenn Sie meine Bearbeitung lesen, und dann korrigieren Sie Ihre Antwort ich wäre mehr als glücklich, es akzeptiert zu markieren. – loostro
Guter Fang, ich vermisste die Tatsache, dass es eine Sammlung von Sammlungen war, das Problem mit nicht testen;) – Kingpin2k