NgRx/Store 4 + Angular 5 Tutorial
November 22, 2017
This page will walk through NgRx/Store 4 and Angular 5 tutorial.
@ngrx/store
is RxJS powered state management that is inspired by Redux. In NgRx, state is composed of a map of action reducer functions. Reducer functions are called with dispatched action and current or initial state and finally reducer returns immutable state. Find the API of @ngrx/store
which we will discuss in our example.
Action:
Action
is state changes. It describes the fact that something happened but does not specify how the applications state changes.
ActionReducerMap:
ActionReducerMap
registers the reducers and then it is configured in application module using StoreModule
.
ActionReducer: It is used to create reducers such as logger.
MetaReducer:
MetaReducer
forms the root meta-reducer that is configured in application module using StoreModule
.
StoreModule:
StoreModule
is a module in @ngrx/store
API that is used to configure reducers in application module.
createFeatureSelector: It creates a feature selector for a state.
createSelector: It creates a selector to wrap a specific state.
Store: It provides
Store.select()
and Store.dispatch()
to work with reducers. Store.select()
selects a selector and Store.dispatch()
dispatches the type of action to reducer.
Now find the complete example step by step.
Contents
- 1. Technologies Used
- 2. Install Angular CLI and NgRx/Store
- 3. Create State
- 4. Create Action Classes
- 5. Create Reducer
- 6. Using createFeatureSelector() and createSelector()
- 7. Using ActionReducerMap
- 8. Using ActionReducer
- 9. Using MetaReducer
- 10. Using StoreModule
- 11. Using Store.select() and Store.dispatch()
- 12. Complete Example
- 13. Run Application
- 14. References
- 15. Download Source Code
1. Technologies Used
Find the technologies being used in our example.1. Angular 5.0.0
2. Angular CLI 1.5.0
3. NgRx/Store 4.1.1
4. TypeScript 2.4.2
5. Node.js 6.11.0
6. NPM 3.10.10
2. Install Angular CLI and NgRx/Store
Find the steps to install Angular CLI.1. Make sure that Node and NPM are installed with minimum version as Node 6.9.x and NPM 3.x.x respectively.
2. Now run command using command prompt.
npm install -g @angular/cli
3. To generate a new project, run the command.
ng new my-app
@ngrx/store
. Go to the directory my-app
using command prompt and run the command.
npm i @ngrx/store --save
5. Find the link for above installation references.
For Angular installation: link
For NgRx/Store installation: link
3. Create State
State is a single immutable data structure. We will create state as following.export interface AppState { articleState: ArticleState; } export interface ArticleState { articles: Article[]; }
4. Create Action Classes
NgRxAction
describes state changes. For each and every action, we need to create a class implementing Action
and define type and payload where payload is optional. Find the sample code snippet.
export const JAVA = 'Java'; export const MY_ARTICLES = 'Favorite_Articles'; export class JavaArticlesAction implements Action { readonly type = JAVA; } export class FavoriteArticlesAction implements Action { readonly type = MY_ARTICLES; constructor(public payload: Article[]) {} }
5. Create Reducer
Reducer describes how the application state changes for any action. We will create reducer as following.export function reducer(state = initialState, action: fromActions.All): ArticleState { switch(action.type) { case fromActions.JAVA: { return {articles: JAVA_ARTICLES}; } case fromActions.ANGULAR: { return {articles: ANGULAR_ARTICLES}; } case fromActions.MY_ARTICLES: { return {articles: action.payload}; } default: { return state; } } }
6. Using createFeatureSelector() and createSelector()
createFeatureSelector()
is used to create a feature selector for any specific state.
export const getArticleState = createFeatureSelector<ArticleState>('articleState');
createSelector()
is used to create selector using feature selector.
export const getArticles = createSelector( getArticleState, (state: ArticleState) => state.articles );
7. Using ActionReducerMap
ActionReducerMap
registers the reducers. We need to create a constant of ActionReducerMap
where we configure our reducers. Then that constant is configured in application module using StoreModule
. Find the sample use of ActionReducerMap
.
export const reducers: ActionReducerMap<AppState> = { articleState: articleReducer.reducer };
8. Using ActionReducer
ActionReducer
is used to create reducers such as logger and then it is configured with MetaReducer
. Find the sample code for logger reducer.
export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> { return function(state: AppState, action: any): AppState { console.log('state', state); console.log('action', action); return reducer(state, action); }; }
9. Using MetaReducer
MetaReducer
is formed as an array of ActionReducer
that we have created. MetaReducer
forms the root meta-reducer that is configured in application module using StoreModule
. By default @ngrx/store
uses combineReducers
to create root meta-reducer. Find the code snippet to create root meta-reducer using MetaReducer
.
export const metaReducers: MetaReducer<AppState>[] = !environment.production ? [logger] : [];
10. Using StoreModule
StoreModule
is a module in @ngrx/store
API that is used to configure reducers in application module.
@NgModule({ imports: [ --- StoreModule.forRoot(reducers, {metaReducers}) ] --- })
11. Using Store.select() and Store.dispatch()
Store.select()
and Store.dispatch()
work with reducers to use them. Store.select()
selects a selector and Store.dispatch()
dispatches the type of action to reducer.
To use
Store
, create a property of Observable
type in component.
articles: Observable<Article[]>
Store
and select the selector.
constructor(private store: Store<ArticleState>) { this.articles = store.select(articleReducer.getArticles); }
showJavaArticles(){ this.store.dispatch(new fromActions.JavaArticlesAction()); }
12. Complete Example
Find the project structure of our example.my-app | |--src | | | |--app | | | | | |--actions | | | | | | | |--article.actions.ts | | | | | |--components | | | | | | | |--article.component.html | | | |--article.component.ts | | | | | |--models | | | | | | | |--article.ts | | | | | |--reducers | | | | | | | |--app.states.ts | | | |--article.reducer.ts | | | |--reducers.ts | | | | | |--app.component.ts | | |--app.module.ts | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json
article.actions.ts
import { Action } from '@ngrx/store'; import { Article } from '../models/article'; export const JAVA = 'Java'; export const ANGULAR = 'Angular'; export const MY_ARTICLES = 'Favorite_Articles'; export class JavaArticlesAction implements Action { readonly type = JAVA; } export class AngularArticlesAction implements Action { readonly type = ANGULAR; } export class FavoriteArticlesAction implements Action { readonly type = MY_ARTICLES; constructor(public payload: Article[]) {} } export type All = JavaArticlesAction | AngularArticlesAction | FavoriteArticlesAction;
export interface Article { id: number; title: string; category: string; } export const JAVA_ARTICLES: Article[] = [ {id: 1, title: 'Java Article 1', category: 'Java'}, {id: 2, title: 'Java Article 2', category: 'Java'}, ] export const ANGULAR_ARTICLES: Article[] = [ {id: 1, title: 'Angular Article 1', category: 'Angular'}, {id: 2, title: 'Angular Article 2', category: 'Angular'}, ] export const FAVORITE_ARTICLES: Article[] = [ {id: 1, title: 'Java Article 1', category: 'Java'}, {id: 2, title: 'Angular Article 2', category: 'Angular'} ]
import { Article } from '../models/article'; export interface AppState { articleState: ArticleState; } export interface ArticleState { articles: Article[]; }
import { createFeatureSelector, createSelector } from '@ngrx/store'; import * as fromActions from '../actions/article.actions'; import { JAVA_ARTICLES, ANGULAR_ARTICLES } from '../models/article'; import { ArticleState } from './app.states'; export const initialState: ArticleState = { articles: []}; export function reducer(state = initialState, action: fromActions.All): ArticleState { switch(action.type) { case fromActions.JAVA: { return {articles: JAVA_ARTICLES}; } case fromActions.ANGULAR: { return {articles: ANGULAR_ARTICLES}; } case fromActions.MY_ARTICLES: { return {articles: action.payload}; } default: { return state; } } } export const getArticleState = createFeatureSelector<ArticleState>('articleState'); export const getArticles = createSelector( getArticleState, (state: ArticleState) => state.articles );
import { ActionReducerMap, ActionReducer, MetaReducer } from '@ngrx/store'; import { AppState } from './app.states'; import * as articleReducer from './article.reducer'; import { environment } from '../../environments/environment'; export const reducers: ActionReducerMap<AppState> = { articleState: articleReducer.reducer }; export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> { return function(state: AppState, action: any): AppState { console.log('state', state); console.log('action', action); return reducer(state, action); }; } export const metaReducers: MetaReducer<AppState>[] = !environment.production ? [logger] : [];
import { Store } from '@ngrx/store'; import { Component } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import * as articleReducer from '../reducers/article.reducer'; import * as fromActions from '../actions/article.actions'; import { ArticleState } from '../reducers/app.states'; import { Article, FAVORITE_ARTICLES } from '../models/article'; @Component({ selector: 'app-article', templateUrl: 'article.component.html' }) export class ArticleComponent { articles: Observable<Article[]> constructor(private store: Store<ArticleState>) { this.articles = store.select(articleReducer.getArticles); } showJavaArticles(){ this.store.dispatch(new fromActions.JavaArticlesAction()); } showAngularArticles(){ this.store.dispatch(new fromActions.AngularArticlesAction()); } showFavoriteArticles(){ this.store.dispatch(new fromActions.FavoriteArticlesAction(FAVORITE_ARTICLES)); } }
<button (click)="showJavaArticles()">Java Articles</button> <button (click)="showAngularArticles()">Angular Articles</button> <button (click)="showFavoriteArticles()">Favorite Articles</button> <ul> <li *ngFor="let article of articles | async"> {{article.id}} - {{article.title}} - {{article.category}} </li> </ul>
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-article></app-article> ` }) export class AppComponent { }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { StoreModule } from '@ngrx/store'; import { AppComponent } from './app.component'; import { ArticleComponent } from './components/article.component'; import { reducers, metaReducers } from './reducers/reducers'; @NgModule({ imports: [ BrowserModule, StoreModule.forRoot(reducers, {metaReducers}) ], declarations: [ AppComponent, ArticleComponent ], providers: [ ], bootstrap: [ AppComponent ] }) export class AppModule { }
13. Run Application
To run the application, find the following steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Install NgRx/Store using NPM command.
4. Run ng serve using command prompt.
5. Now access the URL http://localhost:4200
Find the print screen of output.

14. References
@ngrx/storeAngular QuickStart