Reputation: 1538
I have ngrx store setup that stores currently active user and user's locale. Based on user locale my interceptor will inject X-LOCALE header in all API calls. When action to select user is called I first call separate API to get locale for that user, pass that locale to interceptor and then proceed on calling rest of APIs (at this point X-LOCALE header should be injected). Question: how do I trigger change of the state from user-effects.ts?
user-reducer.ts
export interface UsersState extends EntityState<UserInfo> {
loading: boolean;
currentUser: UserInfo;
locale: string;
}
const usersReducer = createReducer(
INITIAL_STATE,
on(actions.fetchUser,
...bunch of other actions...,
(state) => ({ ...state, loading: true })
);
user-actions.ts
...bunch of actions
export const fetchUser = createAction('[USERS] Fetch User Request', props<{ uuid: string }>());
user-effects.ts
@Injectable()
export class UsersEffects {
fetchUser$ = createEffect(() =>
this.actions$.pipe(
ofType(usersActions.fetchUser),
switchMap(({ uuid }) => {
// here we would read user locale
return this.usersService.fetchUserLocale(uuid).pipe(
map((locale) => {
// ******* here i need to update locale in the state ********
return uuid;
});
}),
);
}),
switchMap((uuid) => {
// all calls from here on should have "X-LOCALE" header injected
return forkJoin([
this.usersService.fetchUser(uuid),
this.usersService.fetchUserCards(uuid),
this.usersService.fetchUserPermissions(uuid),
]).pipe(
map(([userDto, cards, permissions]) => {
const userInfo = { ...userDto, cards, permissions };
return usersActions.fetchUserSuccess({ userInfo });
}),
catchError((error) => of(usersActions.fetchUserFailure({ error }))),
);
}),
),
);
and here locale.interceptor.ts that would inject headers
@Injectable()
export class HttpHeadersInterceptor implements HttpInterceptor {
constructor(private store$: Store<UsersState>) {
this.store$.pipe(select((state) => state.locale)).subscribe((locale: string) => {
this.locale = locale;
});
}
private locale: string;
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const defaultHeaders = { Accept: 'application/json, text/plain' };
if (this.locale) {
defaultHeaders['X-LOCALE'] = this.locale;
}
const authReq = req.clone({ setHeaders: defaultHeaders });
return next.handle(authReq);
}
}
Upvotes: 1
Views: 1574
Reputation: 3193
Well I'm not sure if it's a best practice, but you still can dispatch an action from within the switchMap
, be it directly from the map
or you can change it to tap
since there's no actual mapping, it's just a side effect. Something like this.
switchMap(({ uuid }) => {
// here we would read user locale
return this.usersService.fetchUserLocale(uuid).pipe(
tap((locale) => {
this.store.dispatch(desiredAction({ uuid }));
});
}),
);
As I said, I'm not sure if this is the best practice but in my project I used something similar on couple of occasions.
Upvotes: 2