The document provides an overview of state management in Angular using NgRx. It discusses setting up the store and reducers, accessing state in components, using selectors, handling asynchronous operations with effects, improving data persistence with NX, and connecting related data models. The key steps covered include creating interfaces and initial state, setting up the reducer, importing modules, dispatching actions, using entity adapters for collections, and composing selectors.
2. ABOUT ME
SUWIGYA RATHORE
▸ Front end developer with 7+ years of experience
▸ Working for Non Dutch from Sep 2018
▸ Working on Angular from starting almost 2+ years
▸ Have experience of 2 years with largest customer facing Angular website Air
France & KLM (probably Air Kenya, Joon, Transavia etc.)
▸ Working with dffrntmedia on Earthtoday.com
▸ Loves to travel
▸ Read and experiment about latest technologies
▸ Loves to do binge watching on Netflix
3. RULE 4: REDUX SHOULD THE MEANS OF
ACHIEVING A GOAL, NOT THE GOAL
RULE 5: ALWAYS TREAT ROUTER AS THE SOURCE
OF TRUTH
Victor Savkin
NG-RX
https://blog.nrwl.io/managing-state-in-angular-applications-22b75ef5625f
3
GOLDEN RULES
4. TEXT
AGENDA
▸ State and reducer setup
▸ NG-RX Store setup @ngrx/store
▸ Accessing State in Component
▸ Redux devtools with angular @ngrx/store-devtools
▸ Collection management @ngrx/entity
▸ Dispatching strongly typed actions
▸ State selectors
▸ Async operations with effects @ngrx/effects
▸ Improve server communication with NX data persistence layer @nrwl/nx
▸ Connect Related data models
▸ Folder structure in project & demo
4
5. NG-RX
export interface FeatureAState {
projects: Project[],
selectedProjectId: string | null;
}
export const initialFeatureAState: FeatureAState = {
projects: initialProjects,
selectedProjectId: null
}
export function featureAReducer(
state = initialFeatureAState, action): FeatureAState{
switch(action.type) {
default:
return state;
}
}
)
CREATING INTERFACE FOR FEATURE STATE
5
STATE AND REDUCER
CREATING AN INITIAL STATE
SETTING UP BASIC REDUCER
* Here Project is class of type project
6. NG-RX
import * as fromFeatureA from ‘./featureA.reducer';
export interface AppState {
featureA: fromFeatureA.FeatureAState,
featureB: fromFeatureB.FeatureBState
}
export const reducers: ActionReducerMap<AppState> = {
featureA: fromFeatureA.FeatureAReducer,
featureB: fromFeatureB.FeatureBReducer
}
IMPORTING EVERYTHING FROM FEATURE
6
STORE AND MODULE
MERGING FEATURE STATE IN APP STATE
MERGING FEATURE REDUCER IN APP REDUCER
import { StoreModule } from ‘@ngrx/store’;
import { NgNodule } from '@angular/core';
import { reducers } from ‘.’;
@NgModule({
imports: [StoreModule.forRoot(reducers)]
})
export class StateModule {}
IMPORTING APP REDUCER IN MODULE
IMPORT STORE MODULE AND PASS REDUCERS
import { NgNodule } from '@angular/core';
import { StateModule } from ‘./state.module’;
@NgModule({
imports: [StateModule]
})
export class AppModule {}
IMPORT STATE MODULE IN APP MODULE
7. NG-RX
import { FeatureAState } from ‘./redux/featureA.reducer’;
constructor(
private store: Store<FeatureAState>
){
this.projects$ = store.pipe(
select(‘featureA’),
map((featureAState: FeatureAState) =>
featureAState.projects)
)
}
7
ACCESSING STORE IN COMPONENT
IMPORT FEATURE STATE
INTERFACE
INJECTING STORE AS SERVICE
SELECTING FIELD FROM FEATURE
STATE
8. NG-RX
import { StoreModule } from ‘@ngrx/store’;
import { StoreDevToolsModule } from ‘@ngrx/store-devtools';
import { NgNodule } from '@angular/core';
import { reducers } from ‘.’;
@NgModule({
imports: [StoreModule.forRoot(reducers),
StoreDevtoolsModule.instrument({ maxAge: 10 })]
})
export class StateModule {}
8
REDUX DEVTOOLS
IMPORT STORE DEV TOOLS MODULE
9. NG-RX 9
STRONGLY TYPED ACTIONS
export enum FeatureAActionTypes {
ProjectFetched = '[FeatureA] Fetch’,
ProjectSelected = '[FeatureA] Selected’,
}
import { Action } from '@ngrx/store';
export class SelectProject implements Action {
readonly type = FeatureAActionTypes.ProjectSelected;
constructor(private payload: Project) {}
}
export class FetchProject implements Action {
readonly type = FeatureAActionTypes.ProjectFetched;
}
export type FeatureAAction = SelectProject
| FetchProject
CREATING ENUM FOR ALL ACTIONS
CREATING TYPE OF ACTION WITHOUT PAYLOAD
CREATING TYPE OF ACTION WITH PAYLOAD
EXPORTING ALL ACTIONS AS TYPE
10. NG-RX 10
MODIFY REDUCER TO USE NEW TYPED ACTIONS
import { FeatureAActionTypes } from ‘./featureA.actions’;
export function featureAReducer(
state = initialFeatureAState, action): FeatureAState{
switch(action.type) {
case FeatureAActionTypes.ProjectSelected:
return {
selectedProjectId: action.payload,
projects: state.projects
}
default:
return state;
}
}
)
IMPORT ACTION ENUM
MAPPING CASES WITH NEW ENUMS
11. NG-RX 11
COLLECTION MANAGEMENT
export interface FeatureAState extends EntityState<Project> {
selectedProjectId: string | null;
}
export interface FeatureAState {
projects: Project[],
selectedProjectId: string | null;
}
*OLD STATE AS FROM PREVIOUS SLIDE
CHANGING STATE AS ENTITY STATE
export interface EntityState<T> {
ids: string[] | number[];
entities: Dictionary<T>;
}
export const adapter: EntityAdapter<Project> = createEntityAdapter<Project>();
export const initialFeatureAState: FeatureAState = {
projects: initialProjects,
selectedProjectId: null
}
*OLD INITIAL STATE
export const initialState: ProjectsState = adapter.getInitialState({
selectedProjectId: null
}) PULLING INITIAL STATE OUT OF ADAPTER
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
12. NG-RX 12
BENEFITS OF ENTITY COLLECTION
export function featureAReducer(
state = initialFeatureAState, action): featureAState {
switch (action.type) {
case FeatureAActionTypes.ProjectSelected:
return Object.assign({}, state, { selectedProjectId: action.payload });
case FeatureAActionTypes.ProjectFetched:
return adapter.addMany(action.payload, state);
case FeatureAActionTypes.AddProject:
return adapter.addOne(action.payload, state);
case FeatureAActionTypes.UpdateProject:
return adapter.updateOne(action.payload, state);
case FeatureAActionTypes.DeleteProject:
return adapter.removeOne(action.payload, state);
default:
return state;
}
}
13. NG-RX
ACCESSING ENTITY COLLECTION IN COMPONENT
constructor(
private store: Store<FeatureAState>
){
this.projects$ = store.pipe(
select(‘featureA’),
map((featureAState: FeatureAState) =>
featureAState.projects)
)
}
constructor(
private store: Store<FeatureAState>
){
this.projects$ = store.pipe(
select(‘featureA'),
map(data => data.entities),
map(data => Object.keys(data).map(k => data[k]))
)
}
*OLD WAY ENTITY COLLECTION IN REDUX DEV TOOLS
NEW WAY (ITS BIT COMPLICATED BUT ONLY IN THIS SLIDE)
13