Creating a root store
export const increment = createAction(
'[count] INCREMENT'
);
export const reset = createAction('[count] RESET');
export const initialState = 0
export const counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(reset, state => 0),
);
@NgModule({
declarations: [],
imports: [
StoreModule.forRoot({ count: counterReducer })
],
providers: [],
})
export class CounterStateModule { }
Creating actions
import { createAction, props } from '@ngrx/store';
export const increment = createAction('[Count] INCREMENT');
With payload
export const errorAction = createAction(
'[Error] ERROR',
props<{ error: any }>()
);
Dispatching action
import { Store } from '@ngrx/store';
@Injectable()
export class HeroDispatchers {
// injects the store
constructor(private store: Store<any>) {}
incrementCounter() {
// dispatches increment action
this.store.dispatch(increment());
}
}
Creating Selectors
import {
Store,
createSelector,
createFeatureSelector
} from '@ngrx/store';
/*
if state is stored like
{
entityCache: {
heroesState: {
heroes: [],
loading: false,
error: false
}
}
}
*/
export interface EntityState {
heroesState: {
heroes: [],
loading: false,
error: false
}
}
// selectors
const getEntityState = createFeatureSelector<EntityState>(
'entityCache'
);
const getHeroState = createSelector(
getEntityState,
(state: EntityState) => state.heroesState
);
const getAllHeroes = createSelector(
getHeroState,
(state: any) => state.heroes
);
const getHeroesLoading = createSelector(
getHeroState,
(state: any) => state.loading
);
@Injectable()
export class HeroSelectors {
constructor(private store: Store<EntityState>) { }
// selectors$ observable can be used in html with async pipe
heroes$ = this.store.select(getAllHeroes);
heroState$ = this.store.select(getHeroState);
loading$ = this.store.select(getHeroesLoading);
}
Creating Effects
@Injectable()
export class AllEffects {
// mapping to diff action
effectName$ = createEffect(
() => this.actions$.pipe(
ofType(FeatureActions.actionOne),
map(() => FeatureActions.actionTwo())
)
);
// if no dispatch action after effect resolved
effectNameWithoutDispatch$ = createEffect(
() => this.actions$.pipe(
ofType(FeatureActions.actionOne),
tap(() => console.log('Action One Dispatched'))
),
{ dispatch: false }
// FeatureActions.actionOne is not dispatched
);
getHeroes$ = createEffect(() =>
this.actions$.pipe(
ofType(HeroActions.getHeroes),
switchMap(() =>
// asyncronous call
this.heroDataService.getHeroes().pipe(
map(heroes => HeroActions.getHeroesSuccess({
heroes
})),
catchError(error => of(
HeroActions.getHeroesError({ error }))
)
)
)
)
);
addHero$ = createEffect(() =>
this.actions$.pipe(
ofType(HeroActions.addHero),
concatMap(action =>
this.heroDataService.addHero(
action.hero
).pipe(
map(
hero =>
HeroActions.addHeroSuccess({
hero
})
),
catchError(
error =>
of(HeroActions.addHeroError({ error }))
)
)
)
)
);
}
// in ngModule
EffectsModule.forRoot([AllEffects]),
or
EffectsModule.forFeature([AllEffects]),
Creating a sub module feature store
export const increment = createAction(
'[count] INCREMENT'
);
export const reset = createAction('[count] RESET');
export const initialState = 0
export const counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(reset, state => 0),
);
@NgModule({
declarations: [],
imports: [
StoreModule.forRoot({})
],
providers: [],
})
export class AppModule { }
@NgModule({
declarations: [],
imports: [
StoreModule.forFeature('featureKey', {
count: counterReducer
})
],
providers: [],
})
export class CounterStateModule { }