Sottoscrizione a osservabile nidificato

Ho un’app che effettua una richiesta http per ottenere un elenco di elementi e quindi effettua una richiesta http per ciascun elemento nell’elenco per ottenere informazioni più dettagliate su ciascun elemento. effettivamente:

class ItemsService { fetchItems() { return this.http.get(url) .map(res => res.json()) .map(items => items.map(this.fetchItem(item))); } fetchItem(item: Item) { this.http.get(`${url}/${item.id}`) .map(res => res.json()); } } 

Poi farò qualcosa come itemsService.fetchItems().subscribe(items => console.log(items)) ma quello che succede è che ottengo una serie di osservabili (ogni risposta da fetchItem ). Devo iscrivermi a ciascuno degli osservabili interni così che la richiesta fetchItem venga effettivamente triggersta.

Ho anche provato a usare flatMap invece di map ma sembra avere lo stesso risultato in questo caso. C’è un modo per cui l’osservabile nidificato deve essere abbonato?

Lo farei come il seguente:

 function mockRequest() { return Observable.of('[{"id": 1}, {"id": 2}, {"id": 3}]'); } function otherMockRequest(id) { return Observable.of(`{"id":${id}, "desc": "description ${id}"}`); } class ItemsService { fetchItems() { return mockRequest() .map(res => JSON.parse(res)) .concatAll() .mergeMap(item => this.fetchItem(item)); } fetchItem(item: Item) { return otherMockRequest(item.id) .map(res => JSON.parse(res)); } } let service = new ItemsService(); service.fetchItems().subscribe(val => console.log(val)); 

Guarda la demo dal vivo: http://plnkr.co/edit/LPXfqxVsI6Ja2J7RpDYl?p=preview

Sto usando un trucco con .concatAll() per convertire un array di oggetti come [{"id": 1}, {"id": 2}, {"id": 3}] in valori separati emessi uno da uno {"id": 1} , {"id": 2} e {"id": 3} (al momento è una funzionalità non documentata). Quindi uso mergeMap() per recuperare il loro contenuto in una richiesta separata e unire il risultato nella catena di operatori.

Questo esempio di plnkr stampa su console:

 { id: 1, desc: 'description 1' } { id: 2, desc: 'description 2' } { id: 3, desc: 'description 3' } 

Il problema che probabilmente hai riscontrato è che non hai appiattito abbastanza .

flatMap o mergeMap appiattiranno Observables , Promises , Arrays , persino i generators (non mergeMap su quest’ultimo), quasi tutto ciò che volete lanciarvi.

Quindi quando fai .flatMap(items => items.map(item => this.fetchItem(item)) , stai davvero facendo Observable> => Observable>

Quando fai semplicemente una map stai osservando Observable> => Observable>> .

Quello che devi fare è prima appiattire l’array e quindi appiattire ogni richiesta:

 class ItemsService { fetchItems() { return this.http.get(url) .map(res => res.json()) // Implicitly map Array into Observable and flatten it .flatMap(items => items) // Flatten the response from each item .flatMap((item: Item) => this.fetchItem(item)); } } 

Ora, quanto sopra funziona se non ti dispiace ottenere individualmente ogni risposta all’object. Se hai bisogno di ottenere tutti gli elementi allora dovresti usare forkJoin su tutti i valori interni, ma avresti comunque bisogno di flatMap per appiattire il valore interno risultante:

 fetchItems(): Observable { return this.http.get(url) .map(res => res.json()) .flatMap(items => { const requests = items.map(item => this.fetchItem(item)); return Rx.Observable.forkJoin(requests); }); } 

È ansible suddividere l’array di articoli prima della riga che chiama this.fetchItem . È ansible utilizzare mergeMap su un Observable il cui valore è un array e ogni elemento verrà emesso singolarmente.

 fetchItems() { return this.http.get(url) .map(res => res.json()) .mergeMap(arrItem => this.fetchItem(arrItem)); } 

Edit: Suppongo che avrei dovuto fornire ulteriori spiegazioni. mergeMap è sinonimo di flatMap in rxjs. In genere, si usa flatMap quando la funzione di proiezione restituisce un Observable, ma si appiattiscono anche gli array, quindi, chiamando mergeMap emetterà quindi ogni elemento individualmente, ho pensato che era ciò che OP voleva ottenere. Mi sono anche reso conto che puoi combinare la chiamata di mergeMap e l’ultima chiamata di map , poiché la proiezione per mergeMap verrà chiamata per ogni elemento dell’array, ho apportato le modifiche nel codice sopra.