Reputation: 12453
I want to query to different rest resources and want to combine the results into a single object. Its a user object and every user should contain a single role. I have read I can use flatMap to do this, but I cant make it work:
public getUsers(): Observable<User[]> {
var users: Observable<User[]> = this.someService.getUsers('[A_URL]') // returns Observable<User[]>
.flatMap(res=> {
// for every user call resource 'role' and add it to user object one by one ...
// return this.someService.getRoleByUser('[A_URL_WITH_USERID]')
}.map(res=>{
//add role to user object
// user.role = r;
});
);
return users;
}
Im sorry for the pseudo code, but I really dont understand the syntax. The problem is, that the second resource call needs the id of every user from the first call.
Upvotes: 0
Views: 811
Reputation: 14087
Here's how you can do it :
// This obs emits a SINGLE array of all users.
// Analog to what your `.getUsers()` method would return
const usersArray = Rx.Observable.of([
{ id: 1, name: 'user1' },
{ id: 2, name: 'user2' },
{ id: 3, name: 'user3' }
]);
// Obtain the role(s) for a given user.
const userRoles = function(userId) {
return Rx.Observable.of(`Role for user ${userId}`);
}
const obs = usersArray
// Flatten the array.
// Now each user is emitted as an individual value.
.mergeMap(val => val)
// Fetch user roles
.mergeMap(user => {
return userRoles(user.id).map(role => {
// Put together user + role
user.role = role;
// DO NOT FORGET to return below
return user;
});
})
// At this point, you have individual users with a `user.role` property
// being emitted in the stream.
// Unflatten the array if desired (to obtain a SINGLE array of ALL users)
.reduce((acc, curr) => acc.concat(curr), []);
// Final subscribe
obs.subscribe(val => console.log(val));
JS BIN demoing this code: http://jsbin.com/vucuga/4/edit?js,console
Notes:
flatMap()
is an alias for mergeMap()
. In RxJS 5 (which Angular uses), I believe mergeMap()
is the "official" operator.concatMap()
instead of mergeMap()
.Upvotes: 2