Search code examples
angularngrxngrx-storengrx-effects

Dispatch multiple action from one effect


Hi I want dispatch my new action "LoadConfig" in "loadFullService$" effect.

How to do ?

    @Effect()
loadFullService$: Observable<Action> = this.actions$
    .ofType<servicesActions.LoadFullService>(servicesActions.LOAD_FULL_SERVICE)
    .switchMap(action => {
        return this.apiService
            .loadFullService(action.payload)
            .map((service: Service) => new servicesActions.LoadFullServiceSuccess(service))
            .catch(error => of(new servicesActions.LoadFailureAction(error)));
    })
    ;

@Effect()
loadConfig$: Observable<Action> = this.actions$
    .ofType<servicesActions.LoadConfig>(servicesActions.LOAD_CONFIG)
    .switchMap(action => {
        console.log("action config", action);
        return this.apiService
            .loadConfig(action.id, action.name)
            .map((config: Configuration) => new servicesActions.LoadConfigSuccess(config))
            .catch(error => of(new servicesActions.LoadConfigFailure(error)));
    });

Solution

  • Import the Store service in the constructor.

    constructor(
      private store: Store<StoreType>,
    )
    

    Then inside the action call this.store.dispatch(newAction), with a do operator (usual), anywhere after the ofType().

    @Effect()
    loadFullService$: Observable<Action> = this.actions$
      .ofType<servicesActions.LoadFullService>(servicesActions.LOAD_FULL_SERVICE)
      .do(action => {
        this.store.dispatch(new servicesActions.LoadConfig(action.payload.id, action.payload.name))
      })
      .switchMap(action => {
        return this.apiService
          .loadFullService(action.payload)
          .map((service: Service) => new servicesActions.LoadFullServiceSuccess(service))
          .catch(error => of(new servicesActions.LoadFailureAction(error)));
      });

    Another general approach, which I used to like, is creating a new observable:

    @Effect()
    loadFullService$: Observable<Action> = this.actions$
      .ofType<servicesActions.LoadFullService>(servicesActions.LOAD_FULL_SERVICE)
      .switchMap(action => {
        return this.apiService
          .loadFullService(action.payload)
          .mergeMap((service: Service) => {
            return new Observable(observer => {
              const successAction = new servicesActions.LoadFullServiceSuccess(service));
              const newAction = new servicesActions.LoadConfig(action.id, successAction.name));
              observer.next(successAction);
              observer.next(newAction);
              observer.complete();
          });
        })
        .catch(error => of(new servicesActions.LoadFailureAction(error)));
    });

    The downside is that it adds more churn and following the code gets a bit harder sometimes.

    Finally, a third approach:

    @Effect()
    loadFullService$: Observable<Action> = this.actions$
      .ofType<servicesActions.LoadFullService>(servicesActions.LOAD_FULL_SERVICE)
      .switchMap(action => {
        return this.apiService
          .loadFullService(action.payload)
          .mergeMap((service: Service) => {
            const successAction = new servicesActions.LoadFullServiceSuccess(service));
            const newAction = new servicesActions.LoadConfig(action.id, successAction.name));
            return Observable.from([successAction, newAction]);
          })
          .catch(error => of(new servicesActions.LoadFailureAction(error)));
      });