Getting Error when using mergeMap to merge two sequential subscriptions in Angular

I have two sequential subscriptions:

 this.authService.tokenObs.pipe( map(res => res),mergeMap( token =>
    this.service.getUsers(token).subscribe(res2=>{
            console.log('res2', res2)
  })
));

I get error on token param within merge map:

Argument of type '(token: {}) => Subscription' is not assignable to

parameter of type '(value: {}, index: number) => ObservableInput<{}>'.

Type 'Subscription' is not assignable to type 'ObservableInput<{}>'.

Type 'Subscription' is not assignable to type 'ArrayLike<{}>'.

Property 'length' is missing in type 'Subscription'. (parameter) token: {}

I am using mergeMap for first time so not familiar with this error.

2 answers

  • answered 2018-10-17 08:25 Vivek Kumar

    You need to modify your code like below

    this.authService.tokenObs.pipe(
         map(res => res),
         mergeMap(this.service.getUsers(token))
    ).subscribe(res2=>{
        console.log('res2', res2)
    });
    

    Since you are new to this, Can you take a look at the working example https://stackblitz.com/edit/angular-gsefpz

    import { Component } from '@angular/core';
    import { map, mergeMap } from 'rxjs/operators';
    import { HttpClient} from '@angular/common/http';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      name = 'Angular';
    
      constructor(public http: HttpClient) {
        this.http.get('https://jsonplaceholder.typicode.com/users').pipe(
          map(res => res[0].id),
          mergeMap(id => this.http.get(`https://jsonplaceholder.typicode.com/posts?userId=${id}`))
        ).subscribe(res2 => {
          console.log('res2', res2)
        });
      }
    }
    

    EDIT 2:

    https://stackblitz.com/edit/angular-gsefpz

    Updated the code for handling error. Test the error case you changing it to wrong url.

    import { Component } from '@angular/core';
    import { map, mergeMap, tap, catchError } from 'rxjs/operators';
    import { throwError } from 'rxjs';
    import { HttpClient } from '@angular/common/http';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      name = 'Angular';
    
      constructor(public http: HttpClient) {
        this.http.get('https://jsonplaceholder.typicode.com/users').pipe(
          tap(data => console.log('data >>> ', data)),
          map(res => res[0].id),
          tap(data => console.log('transformed data >>> ', data)),
          mergeMap(id => this.http.get(`https://jsonplaceholder.typicode.com/posts?userId=${id}`)),
          catchError(error => {
            console.log('ERROR >>>> ', JSON.stringify(error));
            return throwError({ status: error.status, errorMsg: error.statusText });
          })
        ).subscribe(res2 => { console.log('res2', res2) }, err => console.log(err));
      }
    }
    

    EDIT 3 for rxjs 5.5 https://stackblitz.com/edit/angular-5-tutorial-yzowvt

    import { Component } from '@angular/core';
    import {HttpClient} from '@angular/common/http';
    import { Observable } from 'rxjs';
    
      constructor(public http: HttpClient) {
        this.http.get('https://jsonplaceholder.typicode.com/users')
        .do(data => console.log('data >>> ', data))
        .map(res => res[0].id)
        .do(data => console.log('transformed data >>> ', data))
        .mergeMap(id => this.http.get(`https://jsonplaceholder.typicode.com/posts?userId=${id}`))
        .catch(error => {
            console.log('ERROR >>>> ', JSON.stringify(error));
            return Observable.throw({ status: error.status, errorMsg: error.statusText });
        })
        .subscribe(res2 => { console.log('res2', res2) }, err => console.log(err));
      }
    

  • answered 2018-10-17 08:40 Dmitriy Ivanko

    If you want use sequence of Observables depends on each other probably you better use "switchMap":

    import { switchMap } from 'rxjs/operators';
    
    this.authService.tokenObs.pipe(
       switchMap((token) => {
          return this.service.getUsers(token);
       }),
       map((user) => {
          // do what you want;
       })
    )
    

    Also you can use "mergeMap":

    import { mergeMap} from 'rxjs/operators';
    
    this.authService.tokenObs.pipe(
       mergeMap((token) => {
          return this.service.getUsers(token);
       }),
       map((user) => {
          // do what you want;
       })
    )
    

    The difference is that if this.authService.tokenObs changes, the "switchMap" will cancel the pending request to the server (this.service.getUsers(token)). But "mergeMap" will not cancel, but continue its execution. P.S: Try research and start use @ngrx-store for better architecture of system with Observables: https://github.com/ngrx/platform