1. Presentation
Presentation: NgRx
https://docs.google.com/presentation/d/1MknPww1MdwzreFvDh086TzqaB5yyQga0PaoRR9bgCXs/edit?usp=sharing
2. Install redux dev tools chrome extension
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en
3. Simple Counter Demo
a) Make a new empty Angular CLI Project
We will make a simple NgRx demo app first before getting deep into it with Nx which will scaffold out an opinionated abstracted version.
ng new ngrx-counter-demo
b) Add NgRx to the app
npm install @ngrx/store @ngrx/store-devtools
c) Add a counter.reducer.ts fill to a new state folder in the app
src/app/state/counter.reducer.ts
export function counterReducer(state, action) {
on('INCREMENT', state => state + 1),
on('DECREMENT', state => state - 1),
on(reset, state => 0),
);
d) Register the reducer and NgRx in the App module
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './state/counter.reducer';
import { StoreDevtoolsModule} from '@ngrx/store-devtools';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
StoreModule.forRoot({ counter: counterReducer }),
StoreDevtoolsModule.instrument({})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
e) Add the HTML to the App component
src/app/app.component.html
<button (click)="increment()">Increment</button>
<div>Current Count: {{ count$ | async }}</div>
<button (click)="decrement()">Decrement</button>
f) Add App component logic
Note: changeDetection: ChangeDetectionStrategy.OnPush to avoid issue with change detection.
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
count$: Observable<number>;
constructor(private store: Store<any>) {
this.count$ = store.pipe(select('counter'));
}
increment() {
this.store.dispatch({ type: 'INCREMENT' });
}
decrement() {
this.store.dispatch({ type: 'DECREMENT' });
}
}
g) Adding action creators
At this point we have no strong types for our actions or our state tree, let's fix that.
Add a file to the state folder called counter.actions.ts
src/app/state/counter.actions.ts
import { Action } from '@ngrx/store';
export enum CounterActionTypes {
Increment = '[Counter Page] Increment',
Decrement = '[Counter Page] Decrement'
}
export const increment = createAction(CounterActionTypes.Increment);
export const decrement = createAction(CounterActionTypes.Decrement);
h) Adding action State interfaces
In the top of the reducer file add a CounterState interface and use it as our new action types in the reducer.
src/app/state/counter.reducer.ts
import { CounterActions, CounterActionTypes } from 'src/app/state/counter.actions';
export interface CounterState {
count: number;
}
export const initialState: CounterState = {
count: 0
};
export function counterReducer(state = initialState, action: CounterActions) {
switch (action.type) {
case CounterActionTypes.Increment:
return {
count: state.count + 1
};
case CounterActionTypes.Decrement:
return {
count: state.count - 1
};
default:
return state;
}
}
i) Add global State interface
This is useful to be able to type your injected store and select state.
src/app/state/appState.ts
import { CounterState } from 'src/app/state/counter.reducer';
export interface AppState {
counter: CounterState;
}
j) Use state interface in app component
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { Increment, Decrement } from 'src/app/state/counter.actions';
import { AppState } from 'src/app/state/appState'; // Added
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
count$: Observable<number>;
constructor(private store: Store<AppState>) { // Added
this.count$ = store.pipe(select(state => state.counter.count)); // Added
}
increment() {
this.store.dispatch(new Increment());
}
decrement() {
this.store.dispatch(new Decrement());
}
}