NgRx used for developing reactive angular applications. @ngrx/store is used to store the data in a container and dispatch it using the set of predefined actions. It will be very useful while developing huge applications which consists of many components and services. In this type of applications if we store the service data in a store we can access the data in all other components using store selectors.

State management is handled using Store for the application, the data is stored in the store and accessed from the store in the form of states.

In this article we are doing how to create a store, dispatch the data into store, get the data from store using selectors.

You can refer the example here:

https://github.com/KtreeOpenSource/AngularExamples/tree/master/ngrxDemo

Step:1 - Create new angular project

Creating the new angular project by running the command

Plain text
1ng new ngrxDemo

This will create ngrxDemo angular repository.

Step:2 - Install ngrx schematics

Install ngrx schematics package by running the command

Shell
1npm install @ngrx/schematics --save-dev

This will install @ngrx/schematics library in node modules.

Step:3 - Setup ngrx schematics Setup the ngrx schematics as default collection in angular projects by running the command

Plain text
1ng config cli.defaultCollection @ngrx/schematics

This command will add the following in angular.json file.

Step:4 - Install @ngrx/core, @ngrx/store, @ngrx/store-devtools Install @ngrx/core, @ngrx/store and @ngrx/store-devtools by running the command using npm

Shell
1npm install @ngrx/core @ngrx/store @ngrx/store-devtools --save

This will install @ngrx/core, @ngrx/store and @ngrx/store-devtools libraries in node modules.

Step:5 - Create store and reducer

Run the command to create store, reducers folders and index.ts file in reducers folder

Plain text
1. ng generate store State --root --statePath store/reducers --module app.module.ts

This command also imports the store module in app.module.ts

JavaScript
1import { StoreModule } from '@ngrx/store';
2import { reducers, metaReducers } from './store/reducers';
3import { StoreDevtoolsModule } from '@ngrx/store-devtools';
4import { environment } from '../environments/environment';
5imports: [
6 BrowserModule,
7 StoreModule.forRoot(reducers, { metaReducers }),
8 !environment.production ? StoreDevtoolsModule.instrument() : []
9],

Step:6 - Create actions

Actions: NgRx actions are used to dispatch the data to store. The actions consists of ‘type‘ property and ‘payload’ as optional property. The actions types are unique they are to store the data in different states.

Create the action by running the command

Plain text
1ng generate action store/actions/user

This will create user.actions.ts in store/actions folder. .src/app/store/actions/user.actions.ts

JavaScript
1import { Action } from '@ngrx/store';
2export enum UserActionTypes {
3 LoadUsers = '[User] Load Users',
4}
5export class LoadUsers implements Action {
6 readonly type = UserActionTypes.LoadUsers;
7}
8export type UserActions = LoadUsers;

Step:7 - Create reducers

Reducers: Reducers are pure functions used to change state. They are not really change states, every time they will copy the state and modify only one or parameters depending on actions.

Create a reducer by running the command

Plain text
1ng generate reducer store/reducers/user --reducers index.ts

This command will create the user.reducer.ts file in store/reducers folder and updates the index.ts file in reducers folder.

Plain text
1 .src/app/store/reducers/user.reducer.ts
JavaScript
1import { Action } from '@ngrx/store';
2export interface State { }
3export const initialState: State = {};
4export function reducer(state = initialState, action: Action): State {
5 switch (action.type) {
6 default:
7 return state;
8 }
9}

This reducer is added to the main reducer.

Plain text
1.src/app/store/reducers.index.ts
JavaScript
1import * as fromUser from './user.reducer';
2export interface State {
3 user: fromUser.State;
4}
5export const reducers: ActionReducerMap<State> = {
6 user: fromUser.reducer,
7};

Step:8 - Create app service

In this step we are creating the service to get the data from users.json

Plain text
1.src/assets/data/users.json
JSON
1[{
2 "id": 1,
3 "userName": "test1",
4 "email": "test1@gmail.com"
5},{
6 "id": 2,
7 "userName": "test2",
8 "email": "test@gmail.com"
9},{
10 "id": 3,
11 "userName": "test3",
12 "email": "test3@gmail.com"
13},{
14 "id": 4,
15 "userName": "test4",
16 "email": "test4@gmail.com"
17}]

Creating the service by running the command

Plain text
1ng generate service /services/app

This will create app service in services folder. Create a action in the service to get the data from users.json file.

Plain text
1.src/app/services/app.service.ts
JavaScript
1import { Injectable } from '@angular/core';
2import { HttpClient } from '@angular/common/http';
3@Injectable({
4 providedIn: 'root'
5})
6export class AppService {
7 constructor(private http: HttpClient) { }
8 getUser() {
9 return this.http.get('assets/data/users.json');
10 }
11}

Import the HttpClientModule in app.module.ts

JavaScript
1import { HttpClientModule } from '@angular/common/http';
2imports: [
3 BrowserModule,
4 StoreModule.forRoot(reducers, { metaReducers }),
5 !environment.production ? StoreDevtoolsModule.instrument() : [],
6 HttpClientModule
7],

Step:9 - Dispatch data to the store

Import the service in the app.component, get the users data from users.json using the service and dispatch the data to the store.

Add the users payload to the LoadUsers action

Plain text
1.src/app/store/actions/user.actions.ts
Plain text
1export class LoadUsers implements Action {
2 constructor(
3 public users: any
4 ) { }
5 readonly type = UserActionTypes.LoadUsers;
6}

Add users state in the user reducer to store the data to the store.

Plain text
1.src/app/store/reducers/user.reducer.ts
JavaScript
1import { UserActions, UserActionTypes } from '../actions/user.actions';
2export interface State {
3 users: any;
4}
5export const initialState: State = {
6 users: []
7};
8export function reducer(state = initialState, action: UserActions): State {
9 switch (action.type) {
10 case UserActionTypes.LoadUsers:
11 return { ...state, users: action.users };
12 default:
13 return state;
14 }
15}

Import the store in app component to dispatch the users data to store.

Plain text
1.src/app/app.component.ts
JavaScript
1import { Component, OnInit } from '@angular/core';
2import { AppService } from './services/app.service';
3import { Store } from '@ngrx/store';
4import * as fromStore from './store/reducers/index';
5import * as UserActions from './store/actions/user.actions';
6@Component({
7 selector: 'app-root',
8 templateUrl: './app.component.html',
9 styleUrls: ['./app.component.css']
10})
11export class AppComponent implements OnInit {
12 constructor(
13 private appService: AppService,
14 public store: Store<fromStore.State>
15 ) { }
16 ngOnInit() {
17 this.appService.getUser().subscribe(res => {
18 this.store.dispatch(new UserActions.LoadUsers(res));
19 });
20 }
21}

Here dispatching the users data into the store by using the users actions.

Step:10 - Create user component

Create the other component and data from the store

Here creating user component by running the command

Plain text
1ng generate component /components/userComponent

This will create user-component in components folder

Plain text
1.src/app/components/user-component/user-component.component.html
HTML / XML
1<p>
2user-component works!
3</p>
Plain text
1.src/app/components/user-component/user-component.component.ts
JavaScript
1import { Component, OnInit } from '@angular/core';
2@Component({
3 selector: 'app-user-component',
4 templateUrl: './user-component.component.html',
5 styleUrls: ['./user-component.component.css']
6})
7export class UserComponentComponent implements OnInit {
8 constructor() { }
9 ngOnInit() { }
10}

Step:11 - Select the users data from the store

Import the store in the user component to get the users data from the store.

Plain text
1.src/app/components/user-component/user-component.component.ts
JavaScript
1import { Store } from '@ngrx/store';
2import * as fromStore from '../../store/reducers/index';
3public users = [];
4constructor(public store: Store < fromStore.State >) { }
5ngOnInit() {
6 this.store.select('user').subscribe(res => {
7 this.users = res.users;
8 })
9}
Plain text
1.src/app/components/user-component/user-component.component.html
HTML / XML
1<div>
2 <ul>
3 <li *ngFor="let user of users">
4 <p>Username: {{user.userName}}</p>
5 <p>Email: {{user.email}}</p>
6 </li>
7 </ul>
8</div>

Step:12 - Create selectors

Selectors: Selectors are pure functions used to get the slice of the state. Using these selectors we can get only particular store, so every time if we update the other stores these selectors subscribe won’t trigger.

Creating selector in user reducer

Plain text
1.src/app/store/reducers/user.reducer.ts
JavaScript
1export const getUsers = (state: State) => state.users;

Import the selector in main reducer

Plain text
1.src/app/store/reducers/index.ts
JavaScript
1import {
2 ActionReducer,
3 ActionReducerMap,
4 createFeatureSelector,
5 createSelector,
6 MetaReducer
7} from '@ngrx/store';
8export const selectUserState = createFeatureSelector < fromUser.State > ('user');
9export const getUserName = createSelector(selectUserState, fromUser.getUsers);

Here getUserName selector is created to get the users from the store

Step:13 - Get the data from selectors

Getting the data from user store using selector

Plain text
1.src/app/components/user-component/user-component.component.ts
JavaScript
1getUsersFromSelectors() {
2 this.store.select(fromStore.getUserName).subscribe(res => {
3 console.log(res);
4 });
5}