Reputation: 73
I got an observable like this:
const original = Observable.of({
a: this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
b: this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
c: this.http.get('https://jsonplaceholder.typicode.com/todos/33')
});
I need to resolve the inner observables and get something like this in the subscribe handler:
{
a: ResponseFromServer,
b: ResponseFromServer,
c: ResponseFromServer,
}
How should I approach this problem?
Thanks.
EDIT: I've figured it out, read below.
It seems that not many people know that *Map operators used to have something called resultSelector
as their second argument. Now in rxjs v6, you can do the same with inner map
, let me show you.
const original = Observable.of({
a: this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
b: this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
c: this.http.get('https://jsonplaceholder.typicode.com/todos/33')
});
const transformed = original.pipe(
mergeMap(sourceValue =>
forkJoin(_.values(sourceValue)).pipe(map(resolvedHttpRequests => {
// here you have access to sourceValue as well as resolvedHttpRequests so you can do manual join to match the data.
}))
)
)
Upvotes: 5
Views: 2444
Reputation: 44
Update for 2020
forkJoin(
// as of RxJS 6.5+ we can use a dictionary of sources
{
google: ajax.getJSON('https://api.github.com/users/google'),
microsoft: ajax.getJSON('https://api.github.com/users/microsoft'),
users: ajax.getJSON('https://api.github.com/users')
}
)
// { google: object, microsoft: object, users: array }
.subscribe(console.log);
https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin
Upvotes: 2
Reputation: 7427
They way you're doing it above (with Observable.of
) is essentially creating a higher-level Observable of lower Observables.
I think a better operator would be forkJoin
, as each of these Observables are cold and finite and forkJoin
captures the first emission of each Observable and emits all values when all are complete:
const original = forkJoin([
this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
this.http.get('https://jsonplaceholder.typicode.com/todos/33')
]);
let result = {a: null, b: null, c: null};
original.subscribe([a,b,c] => result = {a,b,c});
Just note that the emitted item from forkJoin
will be an array whose index match the indices of the Observables passed in.
Upvotes: 1
Reputation: 96889
If the source object already contains Observables you can do it for example like the following (there are obviously multiple ways to do this):
const mockXHRCall = (url: string) => {
return of('response');
};
const original = of({
a: mockXHRCall('https://jsonplaceholder.typicode.com/todos/11'),
b: mockXHRCall('https://jsonplaceholder.typicode.com/todos/22'),
c: mockXHRCall('https://jsonplaceholder.typicode.com/todos/33')
}).pipe(
mergeMap(obj => {
const observables$ = Object.keys(obj).map(key => obj[key].pipe(
map(response => (
{ [key]: response } // wrap the response with eg. { c: ResponseFromC }
)),
));
return merge(...observables$);
}),
scan((acc, wrapped) => (
{ ...acc, ...wrapped }
), {}),
takeLast(1),
).subscribe(console.log);
The scan
collects all responses and merges them into a single object.
Live demo: https://stackblitz.com/edit/rxjs-ffza8b
Upvotes: 1