Enviroment
# Ember : 1.4.0
# Ember Data : 1.0.0-beta.7+canary.b45e23ba
model
Mam uproszczone moim przypadku użyć, aby kwestia łatwiejsze do zrozumienia i anwser. Załóżmy, że mamy 3 modele: Country
, Region
i Area
:Jak zwrócić obietnicę złożoną z modeli zagnieżdżonych w EmberJS z EmberData?
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')
oczekiwane rezultaty
hak modelu Trasa powinna zwracać tablicę obiektów. Tak:
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
Aktualne podejście
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
});
});
}
)};
Błąd
Dostaję Error while loading route: TypeError: Object [object Array] has no method 'then'
który oczywiście pochodzi z tego kodu:
var regions = countries.map(function(country){
return country.get('regions');
});
var areas = regions.then(function(regions){
// regions is not a promise
Jednak powinno to pokazać t on prawdziwy problem mam:
Problem
muszę countries
rozwiązany, aby uzyskać regions
, co z kolei muszę dostać areas
. Sprawdzałem funkcje RSVP.hash
i RSVP.all
, czytając oficjalny interfejs API i oglądając this talk, jednak nie udało mi się utworzyć poprawnego kodu obietnic łańcuchowych, aw ostatecznym then
zmodyfikować zwracany wynik, aby spełnić moje oczekiwania.
Końcowe przemyślenia
Powiedziano mi, że ładowanie danych, jak może to spowodować prośby wielu HTTP i prawdopodobnie to będzie rozwiązane lepiej sideloading, ale:
- w tej chwili używam
FixturesAdapter
, więc żądania HTTP nie są problemem - naprawdę chcę zrozumieć RSVP i obiecuje lepsze
to dlaczego jest impo dla mnie ważne, aby dowiedzieć się, jak to zrobić poprawnie.
Edit 1: zastosowanie zmian sugerowanych przez kingpin2k
Utworzyłem JSBin na moim przykładzie ze zmianami zaproponowanymi przez anwser kingpin2k użytkownika.
Chociaż kod działa, wyniki są ... nieoczekiwany:
- w tablicy
countries
znalazłem zarównocountry
iregion
obiektów. Dlaczego? - Obiekty wydają się być załadowane, ale obszary nie są (zobacz wyniki log konsoli w JSBin). Dlaczego?
Edycja 2: Wyjaśnienie nieoczekiwanego zachowania z Edit1.
Tak więc w końcu zauważyłem, gdzie zbłądziłem ze sprawiedliwej ścieżki Ember. anwser Kingpin2k był ogromny krok naprzód, ale zawiera trochę błąd:
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 = [];
Tak .. Teraz mam wreszcie wszystkie obiekty prawidłowo rozwiązane (krajów, regionów, obszarów) i może kontynuować swoją pracę :)
Cześć, dziękuję za odpowiedź, +1, ponieważ jest to pomocne. Jednak nie mogę oznaczyć jako zaakceptowany (jeszcze), ponieważ wyniki nadal nie są zgodne z oczekiwaniami. Zaktualizowałem moje pytanie i dodałem przykład JSBin. Jeśli byłbyś tak miły, żeby na to spojrzeć, proszę. Pozdrawiam – loostro
Witam, Znalazłem problem. Twój anwser ma nieoczywisty błąd logiczny. Pomogłeś mi dużo z twoim anwser, więc jeśli przeczytasz moją edycję, a następnie poprawisz swoją anwser, byłbym bardziej niż szczęśliwy, gdybym ją zaakceptował. – loostro
Dobry połów, brakowało mi faktu, że był to zbiór kolekcji, problem z nie testowaniem;) – Kingpin2k