Stefan
Stefan

Reputation: 12453

How to nest objects using flatMap?

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

Answers (1)

AngularChef
AngularChef

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.
  • If you need to preserve the order of your users, use concatMap() instead of mergeMap().

Upvotes: 2

Related Questions