Ambiente
# Ember : 1.4.0
# Ember Data : 1.0.0-beta.7+canary.b45e23ba
Modello
Ho semplificato il mio caso d'uso per rendere la questione più facile da capire e anwser. Supponiamo che abbiamo 3 modelli: Country
, Region
e Area
:Come restituire una promessa composta da modelli nidificati in EmberJS con 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')
Risultati attesi
gancio Il modello, lungo la Strada deve restituire un array di oggetti. Come questo:
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
approccio attuale
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
});
});
}
)};
errore
mi sto Error while loading route: TypeError: Object [object Array] has no method 'then'
che proviene ovviamente da questo codice:
var regions = countries.map(function(country){
return country.get('regions');
});
var areas = regions.then(function(regions){
// regions is not a promise
Tuttavia, questo dovrebbe mostrare t egli vero problema che ho:
Il problema
ho bisogno countries
deciso di ottenere il regions
, che a sua volta ho bisogno di ottenere il areas
. Ho controllato le funzioni RSVP.hash
e RSVP.all
, leggendo l'API ufficiale e guardando this talk, tuttavia non riesco a creare il codice corretto per concatenare le promesse e nel finale then
modificare il risultato restituito in modo che corrisponda alle mie aspettative.
Considerazioni finali
mi è stato detto che il caricamento dei dati come questo può causare le richieste molti HTTP e probabilmente questo sarebbe risolto meglio sideloading, ma:
- in questo momento, io uso
FixturesAdapter
, così le richieste HTTP non sono un problema - voglio davvero capire RSVP e migliori promesse
Ecco perché è impo Ringrazio di me per capire come questo dovrebbe essere fatto correttamente.
Modifica 1: applicare le modifiche suggerite da kingpin2k
Ho creato un JSBin per il mio esempio con le modifiche suggerite dai anwser di kingpin2k.
Mentre il codice funziona, i risultati sono ... inaspettata:
- nel
countries
matrice ho trovato entrambe lecountry
eregion
oggetti. Perché? - gli oggetti
country
eregion
sembrano essere caricati, ma l'area non funziona (vedere i risultati del log di console nel JSBin) .. Perché?
Modifica 2: spiegazione del comportamento imprevisto da Edit1.
Quindi ho finalmente notato dove sono andato fuori strada dal sentiero retto di Ember. anwser di Kingpin2k è stato un enorme passo in avanti, ma contiene un piccolo errore:
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 = [];
Quindi .. ora ho finalmente ottenuto tutti gli oggetti correttamente risolti (paesi, regioni, aree) e possono continuare il mio lavoro :)
Ciao, grazie per la risposta, +1 come questo è utile. Tuttavia non posso contrassegnare accettato (ancora), in quanto i risultati non sono ancora come previsto. Ho aggiornato la mia domanda e incluso un esempio JSBin. Se vuoi essere così gentile da dare un'occhiata, per favore fallo. Cheers – loostro
Ciao, ho trovato dov'era il problema. Il tuo anser ha un errore logico non ovvio. Mi hai aiutato molto con il tuo anser quindi se leggi la mia modifica e poi correggi il tuo anser sarei più che felice di segnarlo accettato. – loostro
Una buona cattura, mi mancava il fatto che si trattasse di una serie di raccolte, il problema di non provare;) – Kingpin2k