I have an app that makes one http request to get a list of items and then makes an http request for each item in the list to get more detailed information about each item. Effectively:
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());
}
}
Then I'll do something like itemsService.fetchItems().subscribe(items => console.log(items))
but what ends up happening is I get an array of observables (each response from fetchItem
). I need to subscribe to each of the internal observables as well so that the fetchItem
request actually gets triggered.
I've also tried using flatMap
instead of map but it seems to have the same result in this case. Is there any way for the nested observable to be subscribed to?
You can break up the items array before the line that calls
this.fetchItem
. You can use mergeMap on an Observable whose value is an array and each item will be emitted individually.Edit: I guess I should have provided more explanation.
mergeMap
is synonymous withflatMap
in rxjs. Typically, you use flatMap when your projection function returns an Observable, but it will also flatten arrays as well, so, calling mergeMap will then emit each item individually, I thought that was what OP wanted to achieve. I also realized, you can combine themergeMap
call and lastmap
call, because the projection formergeMap
will be called for each item in the array, I made the changes in the code above.I'd do it like the following:
See live demo: http://plnkr.co/edit/LPXfqxVsI6Ja2J7RpDYl?p=preview
I'm using a trick with
.concatAll()
to convert an array of Objects such as[{"id": 1}, {"id": 2}, {"id": 3}]
into separate values emitted one by one{"id": 1}
,{"id": 2}
and{"id": 3}
(as of now it's an undocumented feature). Then I usemergeMap()
to fetch their content in a separate request and merge it's result into the operator chain.This plnkr example prints to console:
The problem you likely encountered is that you did not flatten enough.
flatMap
ormergeMap
will flattenObservables
,Promises
,Arrays
, evengenerators
(don't quote me on that last one), just about anything you want to throw at it.So when you do
.flatMap(items => items.map(item => this.fetchItem(item))
, you are really just doingObservable<Array<Item>> => Observable<Observable<Item>>
When you just do
map
you are doingObservable<Array<Item>> => Observable<Array<Observable<Item>>>
.What you need to do is first flatten out the Array and then flatten out each request:
Now the above works if you don't mind getting each item response individually. If you need to get all of the items then you should use
forkJoin
on all the inner values, but you would still needflatMap
in order to flatten the resulting inner value: