Search code examples
angularrxjsobservablereactive-programming

RxJS: Get multiple response


I have 'nested' requests, team creation and team avatar uploading, wherein second depends on first and should be sent only if avatar is not null (I've already done this using filter operator). What I need now is to get access to both responses in subscribe method.

this.team.createTeam(teamRequest)
    .pipe(
        filter(() => avatar),
        flatMap(next => this.team.uploadLogo(avatar, next.body.uuid)
    ).subscribe(responses => {
        // so, I want response from both createTeam and uploadLogo in responses
});

Solution

  • You could pipe in a map operator to the inner request and send both the responses. Try the following

    this.team.createTeam(teamRequest)
      .pipe(
        filter(() => avatar),
        flatMap(next => 
          this.team.uploadLogo(avatar, next.body.uuid).pipe(
            map(res => ({ avatar: res, team: next }))
          )
        )
      ).subscribe(responses => {
          // responses.avatar: response from `this.team.uploadLogo(avatar, next.body.uuid)` 
          // responses.team: response from `this.team.createTeam(teamRequest)` 
      });
    

    I've returned an object for illustration. You could return it in any form as per your requirement. Eg. as an array map(res => [ next, res ])).

    Update: iif function instead of filter operator

    If you still want to subscribe to the source observable when the filter condition fails, the quickest way I could think of is to forgo the filter operator and use iif function instead. Try the following

    const upload = (avatar, team): Observable<any> => {
      return this.team.uploadLogo(avatar, next.body.uuid).pipe(
        map(res => ({ avatar: avatar, team: team }))
      );
    };
    
    iif(() =>
      !!avatar,
      this.team.createTeam(teamRequest).pipe(flatMap(next => upload(avatar, next.body.uuid))),
      this.team.createTeam(teamRequest)
    ).subscribe(
      responses => {
        // if `avatar` is null
        // responses: response from `this.team.createTeam(teamRequest)`
        // or
        // if `avatar` is non-null
        // responses.avatar: response from `this.team.uploadLogo(avatar, next.body.uuid)` 
        // responses.team: response from `this.team.createTeam(teamRequest)` 
      }
    );
    

    Update: check avatar inside the flatMap

    As suggested by @bryan60, here is a more cleaner code that uses neither filter nor iif. But rather checks avatar inside the flatMap and returns of(next) if it's null.

    this.team.createTeam(teamRequest).pipe(
      flatMap(next => {
        if (!!avatar) {
          return this.team.uploadLogo(avatar, next.body.uuid).pipe(
            map(res => ({ avatar: res, team: next }))
          );
        }
        return of(next);
      })
    ).subscribe(responses => {
        // if `avatar` is null
        // responses: response from `this.team.createTeam(teamRequest)`
        // or
        // if `avatar` is non-null
        // responses.avatar: response from `this.team.uploadLogo(avatar, next.body.uuid)` 
        // responses.team: response from `this.team.createTeam(teamRequest)` 
    });