cdub
cdub

Reputation: 25751

Chaining actions together in an effect for NgRx (Angular)

I have an effect that worked but I need to chain it with a service that is called in another action after the state gets updated in the first action via a reducer.

The effect is below:

@Effect()
uploadSpecChange$: Observable<Action> = this.actions$.pipe(
    ofType(AppActions.WorkspaceActionTypes.UploadChange),
    switchMap((x: any) => {
        let f = new FileReader();
        f.readAsText(x.payload);
        return fromEvent(f, 'load');
    }),
    map((result: any) => {
        const raw = (<FileReader>result.target).result as string;
        const res = JSON.parse(raw);
        // console.log(res);
        return
            new AppActions.UploadComplete(res),
            new AppActions.PerformCalc()
    })
);

I added the second action but am now getting an Observable error.

Upvotes: 1

Views: 516

Answers (3)

Tony
Tony

Reputation: 20142

You can do like this using switchMap & of operator

@Effect()
dispatchMultiAction$: Observable<Action> = this.actions$.pipe(
    ofType<SomeAction.Dispatch>(someActions.Dispatch),
    switchMap(_ =>
        of(
            new someActions.InitData(),
            new someActions.GetData(),
            new someActions.LoadData()
        )
    )
);

Upvotes: 0

Wandrille
Wandrille

Reputation: 6821

With multiple actions return in an @Effect,

you need to use concatMap instead of map

concatMap((result: any) => {
    const raw = (<FileReader>result.target).result as string;
    const res = JSON.parse(raw);
    return [
        new AppActions.UploadComplete(res),
        new AppActions.PerformCalc()
    ]
})

Upvotes: 0

Vlad Vidac
Vlad Vidac

Reputation: 998

So, if you want to dispatch more than one action from an effect you could return an array of actions and replace your map with a mergeMap:

@Effect()
uploadSpecChange$: Observable<Action> = this.actions$.pipe(
    ofType(AppActions.WorkspaceActionTypes.UploadChange),
    switchMap((x: any) => {
        let f = new FileReader();
        f.readAsText(x.payload);
        return fromEvent(f, 'load');
    }),
    mergeMap((result: any) => {
        const raw = (<FileReader>result.target).result as string;
        const res = JSON.parse(raw);
        // console.log(res);
        return [
            new AppActions.UploadComplete(res),
            new AppActions.PerformCalc()
        ];
    })
);

However, if you want the PerformCalc action to be dispatched once the UploadComplete action is done, you should dispatch PerformCalc as a side effect of UploadComplete:

@Effect()
uploadSpecChange$: Observable<Action> = this.actions$.pipe(
    ofType(AppActions.WorkspaceActionTypes.UploadChange),
    switchMap((x: any) => {
        let f = new FileReader();
        f.readAsText(x.payload);
        return fromEvent(f, 'load');
    }),
    map((result: any) => {
        const raw = (<FileReader>result.target).result as string;
        const res = JSON.parse(raw);
        // console.log(res);
        return new AppActions.UploadComplete(res);
    })
);

@Effect()
uploadComplete$: Observable<Action> = this.actions$.pipe(
    ofType(AppActions.WorkspaceActionTypes.UploadComplete),
    map(() => new AppActions.PerformCalc())
);

Upvotes: 1

Related Questions