Search code examples
angularrxjsngrxngrx-effectsngrx-router-store

NgRx effect based on router needs data from api call


I have an effect that is based on the routerNavigatedAction from @ngrx/router-store.

  pickForm$ = createEffect(() =>
    this.actions$.pipe(
      ofType(routerNavigatedAction),
      filter((navigation) => navigation.payload.routerState.url.startsWith('/requetes/faire-une-requete/')),
      concatLatestFrom(() => this.store.select(selectIsFormSelected)),
      filter(([navigation, isFormSelected]) => isFormSelected),
      map(([navigation, isFormSelected]) => RequestsActions.loadForm({ formId: getAllRouteParameters(navigation.payload.routerState).get('id') }))
    )
  );

So when I navigate to a page, it checks if the id corresponds to a form by using concatLatestFrom and the selectIsFormSelected selector and if so, it triggers a loadForm action.

The problem is that selectIsFormSelected is based on data that is fetched by calling loadAllForms action in the ngOnInit.

When I navigate normally in the site, everything works wonderfully because when I navigate, I already got the data from the backend.

But when I refresh the page, or simply navigate to it from another site, what happens is that the effect above is triggered before the component had time to fetch the required data which means that selectIsFormSelected is always false.

How can I rearrange my effect so that it depends on getting back data from loadAllForms?


Solution

  • If you want to kind of "wait" for another part of the store to have been loaded, you can use a small trick with switchMap, essentially making the previous emission (our source action) to wait for the specific selected part of our state. I think this might satisfy your use case:

         pickForm$ = createEffect(() =>
          this.actions$.pipe(
            ofType(routerNavigatedAction),
            filter((navigation) => 
              navigation.payload.routerState.url.startsWith('/requetes/faire-une-requete/')),
            switchMap((navigation) => this.store.select(selectAllForms).pipe(
              skipWhile(allForms => allForms !== null),
              map(() => navigation),
            )
            concatLatestFrom(() => this.store.select(selectIsFormSelected)),
            filter(([navigation, isFormSelected]) => isFormSelected),
            map(([navigation, isFormSelected]) => RequestsActions.loadForm({ formId: getAllRouteParameters(navigation.payload.routerState).get('id') }))
        ),
      );
    

    This way, you will start listening to this effect only after the allForms have been loaded