7
votes

I'm reaching you here about ngrx effects, what i am tryign to do is to have a function login that distpatch an action type login and an effect on this action will user my service to make api call. After this return token, i want to dispatch two other action one of type getUserMenu and one of type getUserInfo. Those two action will be of different type and have different effect.

I have 3 stores at the end: one for token and auth one for user info one for menu info

i tried things like that :

login = createEffect(
  () =>
  this.actions$
  .pipe(
  ofType(authLogin),
    tap(action => {
    console.log("EFFECT LOGINNNN");
    return this.authService.postLogin(action.username, 
      action.password).pipe(
        map((data: any) => {
          console.log("AUTHTHHTHTH DATATA ", data);
          let props = data.token;
          let payload = {
            token: data.token,
            isAuthenticated: true
          }
      this.store.dispatch(moMenuHttpGetListAction({US_ID: action.username}));  
      this.store.dispatch(userHttpGetInfoAction({US_ID:action.username}));
      this.localStorageService.setItem(AUTH_KEY, payload);
    }))
  })
),
{ dispatch: true }
);

if i set dispatch false login work but no method called for getting user info and user menu but when i set dispatch true i have infinit loop on this same effect the action moMenuHttpGetListAction would look like that :

moMenuHttpGetListEffect = createEffect(
     () => this.actions$.pipe(
        ofType(moMenuHttpGetListAction),
        switchMap(action => {
          console.log("MOMENU LOGINNNN");

          return this.moMenuService.getKmApplications(action.US_ID).pipe(
          map((data: any) => {
            console.log("MOMENU DATATA ", data);
            console.log("MOMENU DATATA ", action.US_ID);
            let payload = {
              MO_MENU: data
            }

          this.store.dispatch(moMenuSetListAction({payload: data}));
           this.localStorageService.setItem(MENU_KEY, payload);
          }))
        })
        ),
    { dispatch: false }
  );

but on this one when i set dispatch to true , i got error compiling. and my action looks like :

import { createAction } from "@ngrx/store";
import { props } from "@ngrx/store";
import { MoMenu, MoMenuState } from "./mo_menu.models";

//TODO CHANGER ME_ID en US_ID
export const moMenuGetErrorAction = createAction("[User] Get Info");
export const moMenuGetIsLoadingAction = createAction("[User] Get Info");

export const moMenuSetErrorAction = createAction('[MoMenu] HTTP GET ACTION',
    props<{error: string}>()
    );
export const moMenuSetLoadingAction = createAction('[MoMenu] HTTP GET ACTION',
    props<{loading: boolean}>()
    );
export const moMenuHttpGetListAction = createAction('[MoMenu] HTTP GETLIST  ACTION',
    props<{US_ID: string}>()
    );
export const moMenuHttpGetListErrorAction = createAction('[MoMenu] HTTP GET ACTION Error',
    props<{error: any}>()
    );

export const moMenuGetListAction = createAction("[MoMenu] Get List");
export const moMenuSetListAction = createAction("[MoMenu] Set Mo Menu List",
    props<{payload: MoMenu[]}>());
export const moMenuDeleteAction = createAction("[MoMenu] Delete List");

those are the two reducers concerned as someone ask me to add :

Ngrx is kinda new to me so i'd love some help on this =)

import { AuthState } from './auth.models';
import { authLogin, 
  authLogout ,
  authGetErrorAction,
  authGetIsLoadingAction,
  authSetErrorAction,
  authSetIsLoadingAction
} from './auth.actions';
import { createReducer, on, Action } from '@ngrx/store';

export const initialState: AuthState = {
  isAuthenticated: false,
  token: undefined,
  isLoading: false,
  HttpResponse: undefined
};

const reducer = createReducer(
  initialState,
    on(authSetErrorAction,  (state, { error }) => ({
    ...state, HttpResponse:  error
  })),
   on(authSetIsLoadingAction,  (state, { isLoading }) => ({
    ...state, isLoading: isLoading 
  })),

  on(authLogin, state => ({ ...state, isAuthenticated: true })),
  on(authLogout, state => ({ ...state, isAuthenticated: false }))
);

export function authReducer(
  state: AuthState | undefined,
  action: Action
): AuthState {
  return reducer(state, action);
}

import { MoMenuState } from "./mo_menu.models";
import { 
  moMenuGetListAction, 
  moMenuDeleteAction,
  moMenuHttpGetListAction,
  moMenuSetListAction,
  moMenuHttpGetListErrorAction,

  moMenuGetErrorAction,
  moMenuGetIsLoadingAction,
  moMenuSetErrorAction,
  moMenuSetLoadingAction
 } from "./mo_menu.actions";
import { createReducer, on, Action } from "@ngrx/store";

export const initialState: MoMenuState = {
    isLoading: false,
  HttpResponse: undefined,
MoMenuItems: null
}

const reducer = createReducer(
  initialState,
  on(moMenuSetErrorAction,  (state, { error }) => ({
    ...state, HttpResponse:  error
  })),
   on(moMenuSetLoadingAction,  (state, { loading }) => ({
    ...state, isLoading: loading 
  })),

  on(moMenuHttpGetListErrorAction, (state, { error }) => (
    undefined)),
   on(moMenuSetListAction, (state, { payload }) => ({
    ...state, MoMenus: payload 
  })),

 on(moMenuHttpGetListAction, (state, { US_ID }) => ({
    ...state
  })),
  on(moMenuGetListAction, state => state),
  on(moMenuDeleteAction, state => state)
);

export function moMenuReducer(
  state: MoMenuState | undefined,
  action: Action
): MoMenuState {
  return reducer(state, action);
}

If some of you guys have any idea for me ?

2
can you add the code for your reducer ?Ashish Sharma
those are the two reducer concernedRazgort
Create a authLoginSuccess action. Dispatch this action on login success in login effect. Now you create two different effect which will listen for authLoginSuccess action, one for your menu and another one for user info. In each new effect you will get chance to call respective api. With this your effects will work independently.Suresh Muttagi

2 Answers

7
votes

This is how I'm dispatching multiple actions as a result of another action in NgRx 8:

@Injectable()
export class MyEffects {
    myAction$ = createEffect(() =>
        this.actions$.pipe(
            ofType(myActions.firstAction),
            switchMap(action => this.someService.getById(action.someId)),
            switchMap((sth: Something) => [
                myActions.firstActionSuccess({ payload: sth }),
                myActions.secondAction({ payload: sth.xyz }),
                // ... more actions
            ])
        )
    );

    constructor(
        private someService: SomeService,
        private actions$: Actions
    ) { }
}

Actions look like this:

export const firstAction = createAction('First Action', props<{ someId: number }>());
export const firstActionSuccess = createAction('First Action Success', props<{ payload: Something }>());
export const secondAction = createAction('Second Action', props<{ payload: string }>());

Hope that helps!

0
votes

You can do like this to dispatch multiple action in your effect

@Effect()
    someEffect$: Observable<Action> = this.actions$.pipe(
        ofType<SOME_ACTION>(SOME_ACTION),
        switchMap(_ =>
            of(
                new myAction.Refresh(),
                new myAction.Clear()
                // another action
            )
        )
    );

Also make sure to have approriate effect that listen on your action to make it work