Search code examples
angularngrxeffect

ngrx effect wait until response received from another state other than initial value


In my angular 10 application, I am having below NgRx effect:

loadValues$ = createEffect(() =>
  this.actions$.pipe(
    ofType(myActions.myActionTypes.LoadData),
    map((action: myActions.loadData) => action.payload),
    withLatestFrom(this._store.pipe(select(fromOtherState.getBioData))),
    switchMap(([action, profile]) => {
      return this.service
        .getDataValues(action["candidateID"], action["hasEnrolled"], profile)
        .pipe(
          map((data) => new myActions.LoadSuccess(data)),
          catchError((err) => of(new myActions.LoadFailed(err)))
        );
    })
  )
);

Here, I want to get value from "fromOtherState.getBioData" where initially it will have null value but later stage another action will fire and will populate this state. So, basically, I want to wait until it has other than a null value, and once it has desired value I want to go ahead and fire success action.

Please note that "fromOtherState.getBioData" has an associated action that will fire parallel when I am firing the above action, as of now it always gives me the profile as null.

Please note that I tried using filter as below: but with no luck:

loadValues$ = createEffect(() =>
  this.actions$.pipe(
    ofType(myActions.myActionTypes.LoadData),
    map((action: myActions.loadData) => action.payload),
    withLatestFrom(this._store.pipe(select(fromOtherState.getBioData))),
    filter(([action, profile]) => {
      return profile != null; // here I want to wait until it has value other than null
    }),
    switchMap(([action, profile]) => {
      return this.service
        .getDataValues(action["candidateID"], action["hasEnrolled"], profile)
        .pipe(
          map((data) => new myActions.LoadSuccess(data)),
          catchError((err) => of(new myActions.LoadFailed(err)))
        );
    })
  )
);

Solution

  • Try to move your selector from withLatestFrom call to the concatMap and try filter it there. This way the rest of the effect should wait until the getBioData has some value. That's the sad part about the withLatestFrom operator, it doesn't care, it just provides.

    loadValues$ = createEffect(() =>
      this.actions$.pipe(
        ofType(myActions.myActionTypes.LoadData),
        map((action: myActions.loadData) => action.payload),
        concatMap((action) =>
          this_store.select(fromOtherState.getBioData).pipe(
            filter(profile => !!profile),
            map((profile) => ({action, profile}))
          )
        )
        switchMap(({action, profile}) => {
          return this.service
            .getDataValues(action["candidateID"], action["hasEnrolled"], profile)
            .pipe(
              map((data) => new myActions.LoadSuccess(data)),
              catchError((err) => of(new myActions.LoadFailed(err)))
            );
        })
      )
    );
    

    It's bit difficult for me to test it, but I have a feeling this approach might work.