Ricardo
Ricardo

Reputation: 1520

How do you dispatch multiple Actions from an Effect in Ngrx

I'm following the basic effect pattern of Ngrx using datapersistence.pessimisticUpdate() for a post to the server like so.

@Effect()
myUpdateCompleted$ = this.dataPersistence.pessimisticUpdate(MyActions.UpdateData, {

    run: (action: UpdateData, state: MyDataPartialState) => {
        return this.myDataService.updateData(action.payload)
            .pipe(
                map(result => ({
                    type: MyActions.ReloadData,
                    payload: result,
                })
                )
            );
    },

    onError: (action: UpdateData, error) => {
        console.error('Error', error);
        return new HandleUpdateDataError(error);
    }
});

I'd like to reload data after that update is done. However, before I reload the data I'd like to notify the user that the update finished and now a data reload is executing. My intended approach was to dispatch two actions after the update finishes. One action to notify the user (dispatch MyActions.NotifyMessage) and another to reload the data (dispatch MyActions.ReloadData). However, the datapersistence methods only allow for a single action to be returned and no provision for an array of actions to be dispatched. I've tried the solution posted on this question:

Dispatch multiple, ordered, actions from effect in NGRX

But I get the error:

Property 'payload' does not exist on type 'Action' 

How do I go about doing this? Should I dump the use of the datapersistence functions and return multiple actions in some other way? If so, how can I do that within an Ngrx Effect? Any help would be highly appreciated.

Upvotes: 1

Views: 3013

Answers (3)

Eduardo
Eduardo

Reputation: 11

This work for me:

    throwMultipleActions$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ThrowMultipleActions),
            switchMap(({ payload }) => {
                const actionsToThrow = [];
                payload.forEach(el => {
                    actionsToThrow.push(attachAction(el));
                });
                return actionsToThrow
            }),
            catchError((error) => {
                return observableOf(ThrowMultipleActionsFail({ payload: error }));
            })
        ));
    const attachAction = (el: { actionName: string; payload: any }) => {
        const { actionName, payload } = el;
        switch (actionName) {
            case 'SetFilter':
                return SetFilter({ payload });
            case 'GetAllCompanys':
                return GetAllCompanys({ payload });
            default:
                break;
        }
    };

//Example throwing multiple actions:

this.clientStore.dispatch(
            ThrowMultipleActions({
                payload: [
                    { actionName: 'SetFilter', payload: this.filtros },
                    { actionName: 'GetAllCompanys', payload: this.filtros },
                ],
            })
        );

Upvotes: 0

Ricardo
Ricardo

Reputation: 1520

This did it.

import { Actions } from '@ngrx/effects';

    @Effect()
    save$: Observable<Notification | SaveSuccess> = this.actions$
        .ofType(MyActions.UpdateData)
        .pipe(
            switchMap(action => this.myService.save(action.payload)),
            switchMap(res => {
                return [
                   new Notification('save success'), 
                   new SaveSuccess(res)
                ];
        })
    );

this.actions$ requires import { Actions } from '@ngrx/effects';

In addition to this, if you have to pass data from the original action payload, you'd have to use concatMap like so (Note - the example below just passes the whole action instead of just the payload):

import { Actions } from '@ngrx/effects';

    @Effect()
    save$: Observable<Notification | SaveSuccess> = this.actions$
        .ofType(MyActions.UpdateData)
        .pipe(

            // The difference is in this switchMap
            switchMap(action => { 
                return this.myService.save(action.payload).pipe(
                    concatMap<any, UpdateData>(() => of(action)) 
                );                
            }),

            switchMap(origAction => {
                return [
                   new Notification('save success'), 
                   new SaveSuccess(origAction.payload)
                ];
        })
    );

Hope this helps anyone struggling with this as I did.

Upvotes: 0

timdeschryver
timdeschryver

Reputation: 15497

You can use `switchMap for this - Dispatching Multiple Actions from NGRX Effects

@Effect() 
save = this.update$.pipe(
   map(action => action.payload),
   switchMap(payload => this.myService.save(payload)),
   switchMap(res => [
       new Notification('save success'),
       new SaveSuccess(res)
   ])
);

Upvotes: 2

Related Questions