fenix2222 fenix2222 - 1 month ago 12
TypeScript Question

Trying to merge multiple observables into one in Angular 2 project

I am trying to get my head around Observables in RxJs. I have a page where I need to display all users for a specific site. User and SiteUser entities are in separate API endpoints. I have the following endpoints

userService.getSiteUsers(siteId: string): Observable<SiteUser[]>;


where

export class SiteUser {
site_id: string;
user_id: string;
}


and

userService.getUser(user_id: string): Observable<User>;


where

export class User {
id: string;
name: string;
email: string;
....
}


So I have to do the following:


  1. Call siteUsers API to get all user ids for specific site

  2. For each user id make a getUser API call to get user details



I can easily do this

let users: User[] = []; // this is bound in html view to a table
this.userService.getSiteUsers("my site id")
.subscribe((siteUsers) => {
for (let siteUser of siteUsers) {
this.userService.getUser(siteUser.user_id)
.subscribe((user) => {
users.push(user);
});
}
});


But this approach feels dirty or cumbersome. I am sure there is a much cleaner Observable way of doing it. I am very new to Observables, but as far as I understand I should be able to do something like this (not selectMany and mergeAll function are just my guesses, I tried it and it didn't work, I couldn't even find selectMany in rxjs library))

Get site user observable array -> for each element in observable array create user observable -> merge them all into observable array of users -> subscribe, so something like this:

this.userService.getSiteUsers("my site id")
.selectMany((siteUser) => this.userService.getUser(user))
.mergeAll()
.subscribe((users) => {
this.users = users;
});


Can someone please help, I can't get it working

EDIT------

Maybe something like this

this.userService.getSiteUsers("my site id")
.switchMap(
(siteUsers) => {
let userQueries: Observable<User>[] = [];
for (let siteUser of siteUsers) {
userQueries.push(this.userService.getUser(siteUser.user_id));
}

return Observable.forkJoin(userQueries);
}
)
.subscribe((users) => {
this.users = users;
});

Answer

You should use the .flatMap() / .mergeMap() operator if one http call depends on another http call.

For example in your case something like this would do,

this.userService.getSiteUsers("my site id")
.switchMap(
   (siteUsers) => {
     let userQueries: Observable<User>[] = [];
     for (let siteUser of siteUsers) {
        userQueries.push(this.userService.getUser(siteUser.user_id));
     }

     return Observable.forkJoin(userQueries);
   }
)
.subscribe((users) => {
    this.users = users;
});