Reputation: 51
I'm trying to make multiple http requests depends of previous one and combine returned data to final result object.
I have two endpoints:
/properties
which returns me something like that:
{
"data":[
{
"id":1,
"name":"Property 1",
"units":[
{
"id":1,
"name":"Property 1 - Unit 1",
},
{
"id":2,
"name":"Property 1 - Unit 2",
},
],
},
{
"id":2,
"name":"Property 2",
"units":[
{
"id":3,
"name":"Property 2 - Unit 3",
}
]
}
]
}```
/properties/${property.id}/units/${unit.id}/bookings
which returns me some object related to previous request for instance:
{
"data": {
"id": 1
},
"meta": {
"count": 5
}
}
What I would like to achieve is combine those two responses into one on single unit level. Like this:
{
[
...
{
"id":2,
"name":"Property 2",
"units":[
{
"id":3,
"name":"Property 2 - Unit 3",
"bookings_meta": {
"count":5
}
}
]
}
]
}
I created something like this and it works fine:
list(): Observable<any> {
return this.http.get('/properties').pipe(
map((results: any) => results.data),
flatMap((properties: any[]) => {
if (properties.length > 0) {
return forkJoin(properties.map((property: any) => {
return forkJoin(property.units.map((unit: any) => {
return this.http.get(`/properties/${property.id}/units/${unit.id}/bookings`).pipe(
map(({ meta }) => {
unit.bookings_meta = meta
return unit;
})
)
}))
}))
}
return of([]);
})
);
}
this.list().subscribe(response => {
console.log(response)
})
But I think it is not in 100% correct solution.
I'm feeling there are too many forkJoin
and maybe it shouldn't be there flatMap
?
The requests for bookings between units should be independent.
Some one have an idea how to improve the code above?
Upvotes: 1
Views: 1465
Reputation: 51
Finally I found an answer for the observable hell :)
list(): Observable<any> {
return this.http.get('/properties').pipe(
map((results: any) => results.data),
mergeMap((properties: any[]) => forkJoin(
properties.map((property: any) => forkJoin(
property.units.map((unit: any) => this.http.get(`/properties/${property.id}/units/${unit.id}/bookings`).pipe(
map(({ meta }) => ({ ...unit, meta }))
))
)).pipe(map((units: any[]) => ({ ...property, units }))))
))
);
}
Cheers!
Upvotes: 1
Reputation: 38209
forkJoin
will return data when all calls are finished and return result
const combined = Observable.forkJoin(
this.http.get('https:// ...properties').map((res: Response) => res.json()),
this.http.get('https:// ..properties/${property.id}/units/${unit.id}/bookings')
.map((res: Response) => res.json())
)
combined.subscribe(latestValues => {
console.log( "latestValues" , latestValues );
});
UPDATE:
It is possible to transform items emitted by an Observable into another Observable by using the flatMap
operator. It creates an inner Observable
and flats its result to the outer stream.
list(): Observable<any> {
return this.http.get('/properties')
.flatMap(properties => properties.map(p => p.units.map(unit =>
{
this.http.get(`/properties/${property.id}/units/${unit.id}/bookings`)
})))
.subscribe(res => { // some actions });
}
Upvotes: 1