Search code examples
angularngrxngrx-storengrx-effects

Correctly mapping in effect of ngrx by calling multiple services and actions


I have multiple effects and trying to combine it in one since there is an issue when dispatching the actions separately. I am trying to execute sequentially after checking the value returned from 1st service call. I have below three separate effects:

@Effect()
loadNqInclDResponse$ = 
  this.actions.ofType(fromActions.LOAD_NQINCLD_RESPONSE).pipe(
    switchMap(() => {
      return this.cinReuseService.WmsInCl().pipe(
        map(responseNqinclD => new 
          fromActions.LoadNqInclDResponseSuccess(responseNqinclD)),
        catchError(error => {
          const err: responseNqinclD = {
            httpStatus: "FALSE",
            message: "ERROR"
          };
          return of(new fromActions.LoadNqInclDResponseFail(err));
        })
      );  
    })
  );


@Effect()
loadCinReuseService$ = 
  this.actions.ofType(fromActions.LOAD_CIN_REUSE_SERVICE).pipe(
    switchMap(() => {
      return this.cinReuseService.cinReuseServiceCall().pipe(
        map(responseReuseService => new 
          fromActions.LoadCinReuseServiceSuccess(responseReuseService)),
        catchError(error => {
          const err: responseReuseService = {
            status: "FALSE",
            message: "ERROR"
          }
          return of(new fromActions.LoadCinReuseServiceFail(err))
        })
      );
    })
  )

@Effect()
loadCaseReuseService$ = 
  this.actions.ofType(fromActions.LOAD_CASE_REUSE_SERVICE).pipe(
    switchMap(() => {
      return this.cinReuseService.caseReuseServiceCall().pipe(
        map(responseReuseService => new fromActions.LoadCaseReuseServiceSuccess(responseReuseService)),
        catchError(error => {
          const err: responseReuseService = {
            status: "FALSE",
            message: "ERROR"
          };

          return of(new fromActions.LoadCaseReuseFail(err));
        });
      )
    });

);

I expected single effect to be as below:

@Effect()
loadNqInclDResponse1$ = 
  this.actions.ofType(fromActions.LOAD_NQINCLD_RESPONSE).pipe(
    switchMap(() => {
      return this.cinReuseService.WmsInCl().pipe(
        map(nqinclD => {        
          // new fromActions.LoadNqInclDResponseSuccess(nqinclD);
          if (nqinclD.httpStatus === '200') {
            switchMap(() => {
              return this.cinReuseService.cinReuseServiceCall().pipe(
                map(cin => new fromActions.LoadCinReuseServiceSuccess(cin)));
            }),
            switchMap(() => {
              return this.cinReuseService.caseReuseServiceCall().pipe(
                map(cse => new fromActions.LoadCaseReuseServiceSuccess(cse)));
            })
          }
        }),
        catchError(error => {
          let err: responseNqinclD = {
            httpStatus: "FALSE",
            message: "ERROR"
          };
          return of(new fromActions.LoadNqInclDResponseFail(err))
        })
      )
    })
  )
}

On a button click, I am receiving error as below when dispatching fromActions.LOAD_CIN_REUSE_SERVICE:

Error: Effect "CinReuseEffect.loadNqInclDResponse1$" dispatched an invalid action: undefined

TypeError: Actions must be objects

Service call wmsInCl() has below code for testing purpose at the moment:

WmsInCl(): Observable<responseNqinclD> {
  var body: Body;
  var response: responseNqinclD;
  console.log("1111111" + body);
   response = {
    httpStatus: "200",
    message: "SUCCESS"
  }
  console.log(response);
  return of(response);
 }
}

Thank you in advance!


Solution

  • Try this:

    @Effect()
    loadNqInclDResponse1$ = 
      this.actions.ofType(fromActions.LOAD_NQINCLD_RESPONSE).pipe(
        switchMap(() => this.cinReuseService.WmsInCl()),
        switchMap((nqinclD) => {
          if(nqinclD.httpStatus === '200') {
            return combineLatest([
              of(nqinclD),
              this.cinReuseService.cinReuseServiceCall(),
              this.cinReuseService.caseReuseServiceCall()
            ]);
          }
          const err: any = {
            httpStatus: "FALSE",
            message: "ERROR"
          };
          return of(err);
        }),
        switchMap(value => {
          if(Array.isArray(value)) {
            const [nqinclD,cin,cse] = value;
            return [
              new fromActions.LoadNqInclDResponseSuccess(nqinclD),
              new fromActions.LoadCinReuseServiceSuccess(cin),
              new fromActions.LoadCaseReuseServiceSuccess(cse),
            ];
          }
    
          return [
            new fromActions.LoadNqInclDResponseFail(value),
            new fromActions.LoadCinReuseServiceFail(value),
            new fromActions.LoadCaseReuseFail(value)
          ];
        }),
        catchError(error => {
          const value = {
            httpStatus: "FALSE",
            message: "ERROR"
          };
          return from([
            new fromActions.LoadNqInclDResponseFail(value),
            new fromActions.LoadCinReuseServiceFail(value),
            new fromActions.LoadCaseReuseFail(value)
          ]);
        })
      );
    

    switchMap has a useful behavior: when it's returning an array of values, it internally converts the elements of the array to individual observables (one observable for each element of the array), like this:

    of(1).pipe(switchMap((_) => [1,2,3]).subscribe(console.log);
    // 1
    // 2 
    // 3