Angular State Management Made Simple with NgRx

Angular NgRx Store is a client-side data management pattern used in large applications. The Ngrx/Store implements the Redux pattern using RxJS observables of Angular. The ngrx/store builds on the concepts made famous by Redux and supercharges it with the backing of RxJS. 

Redux

Redux is a predictable state container for JavaScript applications. Redux following the Unidirectional flow. Redux has a Single Store. 

Redux cannot have multiple stores.

The store is divided into various state objects. So all we need is to maintain the single store, or we can say the only source of truth.

Three Principles Of Redux

  1. Single source of truth.
  2. The state is read-only.
  3. Changes are made with pure functions.

Actions

Actions are payloads of information that send data from your application to your store. Actions are the payload that contains needed information to alter your store. Action has two properties.

1) Type

2) Payload

We will take these properties as an argument of a reducer to change the current state.

Action Creators

Action creators are precisely the functions that create actions.

function addTodo(text) {
  return {
    type: ADD_TODO,
    payload: text 
  }
}

Reducers

Actions describe that something happened but don’t specify how the application’s state changes in response. That is the job of reducers. 

Reducers are the pure functions that know what to do with a given action and the previous state of your application. The reducers will take the previous state from your store, apply a pure function, and transform it into a new state. Pure Functions are based on Functional Programming.

Handling Actions

(previousState, action) => newState

Dispatcher

Dispatchers are an entry point for your application to dispatch your action. For example, in Ngrx, there is a dispatch method directly on the store.

Store

A store is an object that brings them together. You must note that you will only have a store in an NgRX application. Therefore, you must use reducer composition instead of many stores to split your data into a separate handling logic file.

Reactive Programming

Reactive programming is how applications handle events and data flow in your angular applications. In reactive programming, you design your components and other pieces of your software to react to those changes.

The javascript library for reactive programming is RxJS. By providing observables and many operators to transform incoming data, this library will help you handle events in your application. It is a component or service that reacts to data modifications.

Here is the step-by-step guide to implement the ngrx store in Angular.

Step 1: Configure the Angular Project.

Install AngularCLI globally on our PC by typing the following command.

npm install -g @angular/cli

Fire the following command to create a project.

ng new ngrxstore

Create one component called blockchain by typing the following command.

ng g c blockchain

Create one form in this component. First, in the src >>  index.html file, add the Bootstrap CSS file.

<link rel="stylesheet" href="assets/css/bootstrap.min.css">

Add the following HTML code to the blockchain.component.html file.

<div class="panel panel-primary">
    <div class="panel-body">
      <form>
        <div class="form-group">
          <label class="col-md-4">Coin Name</label>
          <input type="text" class="form-control" name="coin_name"/>
        </div>
        <div class="form-group">
          <label class="col-md-4">Coin Price</label>
          <input type="text" class="form-control" name="coin_price"/>
          </div>
          <div class="form-group">
            <button type="submit" class="btn btn-primary">Add</button>
          </div>
      </form>
    </div>
  </div>

Update the app.component.html file to show this form.

<h3 align="center">Angular ngrx/store Tutorial</h3>
<div class="container">
  <app-blockchain></app-blockchain>
</div>

Go to the CMD and hit the following command.

ng serve --open

It will host our Angular application at port no: 4200

ngrx store tutorial

Step 2: Create a model for our application.

We need to define our application’s schema. So create one file inside the blockchain folder called blockchain.model.ts file.

export interface Blockchain {
  name: string;
  price: number;
}

I have defined the schema of our application.

Step 3: Install the ngrx library.

In your package.json file, update the following packages.

"@ngrx/core": "^1.2.0",
"@ngrx/effects": "^4.1.1",
"@ngrx/store": "^4.1.1",

Go to the terminal and hit the following command.

npm install

Step 4: Create a reducer for our application.

Go to the app folder and create one folder called reducers. In that folder, make one file called the blockchain.reducer.ts

// blockchain.reducer.ts

import { Blockchain } from './../blockchain/blockchain.model';
import { Action } from '@ngrx/store';

export const ADD_COIN = 'ADD_COIN';

export function addCoinReducer(state: Blockchain[] = [], action) {
  switch (action.type) {
    case ADD_COIN:
        return [...state, action.payload];
    default:
        return state;
    }
}

Here, I have defined the action type and reducer function. As we have previously discussed, a Reducer is a pure function.

So the pure function always returns a new state. It does not modify the old state of the application.

Step 5: Configure the ngrx store in our application.

Go to the app.module.ts file and include the StoreModule in our application.

// app.module.ts

import { StoreModule } from '@ngrx/store';
import { addCoinReducer } from './reducers/blockchain.reducer';

 imports: [
    BrowserModule,
    StoreModule.forRoot({blockchain: addCoinReducer})
],

Here, I have passed the reducer to the store now; if we need to change our application, state the dispatch the actions, change the state, and update the store.

Step 6: Add a click event to the form.

Attach a button and click event listener to the form. So our whole blockchain.component.html looks like this.

<div class="panel panel-primary">
    <div class="panel-body">
      <form>
        <div class="form-group">
          <label class="col-md-4">Coin Name</label>
          <input type="text" class="form-control" #name/>
        </div>
        <div class="form-group">
          <label class="col-md-4">Coin Price</label>
          <input type="text" class="form-control" #price />
          </div>
          <div class="form-group">
            <button (click) = "addCoin(name.value, price.value)" class="btn btn-primary">Add</button>
          </div>
      </form>
    </div>
  </div>

Write the addCoin() function into the blockchain.component.ts file.

// blockchain.component.ts

import { Blockchain } from './blockchain.model';
import { AppState } from './../app.state';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';

@Component({
  selector: 'app-blockchain',
  templateUrl: './blockchain.component.html',
  styleUrls: ['./blockchain.component.css']
})
export class BlockchainComponent implements OnInit {

  constructor(private store: Store<AppState>) { }

  addCoin(name, price) {
    this.store.dispatch({
      type: 'ADD_COIN',
      payload: <Blockchain> {
        name: name,
        price: price
      }
    });
  }

  ngOnInit() {
  }

}

To implement the Global AppState in our application. So, the next step will be to implement it.

Step 7: Define the AppState.

In the app folder, create one file called app.state.ts.

// app.state.ts

import { Blockchain } from './blockchain/blockchain.model';

export interface AppState {
  readonly blockchain: Blockchain[];
}

Step 8: Add validation in the form.

Include the ReactiveFormsModule in the app.module.ts file.

// app.module.ts

import { ReactiveFormsModule } from '@angular/forms';

imports: [
    BrowserModule,
    StoreModule.forRoot({blockchain: addCoinReducer}),
    ReactiveFormsModule
  ],

The blockchain.component.ts file looks like this.

// blockchain.component.ts

import { FormGroup,  FormBuilder,  Validators } from '@angular/forms';
import { Blockchain } from './blockchain.model';
import { AppState } from './../app.state';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';

@Component({
  selector: 'app-blockchain',
  templateUrl: './blockchain.component.html',
  styleUrls: ['./blockchain.component.css']
})
export class BlockchainComponent implements OnInit {
  angForm: FormGroup;
  constructor(private store: Store<AppState>, private fb: FormBuilder) {
    this.createForm();
  }
  createForm() {
    this.angForm = this.fb.group({
      name: ['', Validators.required ],
      price: ['', Validators.required ]
   });
  }

  addCoin(name, price) {
    this.store.dispatch({
      type: 'ADD_COIN',
      payload: <Blockchain> {
        name: name,
        price: price
      }
    });
  }

  ngOnInit() {
  }

}

Here, I have written the validation logic for both input types.

Our view blockchain.component.html file looks like this.

<div class="panel panel-primary">
  <div class="panel-heading">
    {{ title }}
  </div>
  <div class="panel-body">
    <form [formGroup]="angForm" novalidate>
      <div class="form-group">
        <label class="col-md-4">Coin Name</label>
        <input type="text" class="form-control" formControlName="name" #name />
      </div>
      <div *ngIf="angForm.controls['name'].invalid && (angForm.controls['name'].dirty || angForm.controls['name'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['name'].errors.required">
          Name is required.
        </div>
      </div>
      <div class="form-group">
        <label class="col-md-4">Coin Price</label>
        <input type="text" class="form-control" formControlName="price" #price/>
      </div>
      <div *ngIf="angForm.controls['price'].invalid && (angForm.controls['price'].dirty || angForm.controls['price'].touched)" class="alert alert-danger">
        <div *ngIf="angForm.controls['price'].errors.required">
          Price is required.
        </div>
      </div>
        <div class="form-group">
          <button (click)="addCoin(name.value, price.value)" [disabled]="angForm.pristine || angForm.invalid" class="btn btn-primary">Add</button>
        </div>
    </form>
  </div>
</div>

If everything is fine, you can add the data to the store.

We need to display that data on the front end to verify it. So make one view component to display the data.

Step 9: Create a view component to display data.

Go to the terminal and hit the following command.

ng g c display

Subscribe to the store and fetch the data. So write the following code into the display.component.ts file.

// display.component.ts

import { Blockchain } from './../blockchain/blockchain.model';
import { Component, OnInit } from '@angular/core';
import { AppState } from './../app.state';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-display',
  templateUrl: './display.component.html',
  styleUrls: ['./display.component.css']
})
export class DisplayComponent implements OnInit {

  coins: Observable<Blockchain[]>;
  constructor(private store: Store<AppState>) {
    this.coins = this.store.select(state => state.blockchain);
   }

  ngOnInit() {
  }

}

Write the HTML view for this component.

<table class="table table-hover" *ngIf="coins!= 0">
  <thead>
  <tr>
      <td>Coin Name</td>
      <td>Coin Price</td>
  </tr>
  </thead>
  <tbody>
      <tr *ngFor="let coin of coins | async">
          <td>{{ coin.name }}</td>
          <td>{{ coin.price }}</td>
      </tr>
  </tbody>
<table class="table table-hover" *ngIf="coins!= 0">
  <thead>
  <tr>
      <td>Coin Name</td>
      <td>Coin Price</td>
  </tr>
  </thead>
  <tbody>
      <tr *ngFor="let coin of coins | async">
          <td>{{ coin.name }}</td>
          <td>{{ coin.price }}</td>
      </tr>
  </tbody>

Add this view in the app.component.html file.

<h3 align="center">Angular ngrx/store Tutorial</h3>
<div class="container">
  <app-blockchain></app-blockchain>
  <br/>
  <app-display></app-display>
</div>

Add the Coin name and price; you will see it will store in the ngrx/store, fetch that data from the store, and display that information.

Angular 5 ngrx store tutorial with example from scratch

That’s it!

Fork Me On Github

15 thoughts on “Angular State Management Made Simple with NgRx”

  1. Please help me to delete 1 item on store.

    I try this:

    case REMOVE_FROM_CART: {
    const index = state.findIndex((res) => res.id == action.payload.id);
    return [
    …state.slice(0, index),
    …state.slice(index + 1)
    ];
    }

    removeFromCart(payload) {
    this.store.dispatch({
    type: ‘REMOVE_FROM_CART’,
    payload: payload
    })
    }

    but this only change the position of my item

    Reply
  2. there is a concern, if browser gets refreshed than store values gets cleared. is there anything that i can add to preserve the value of store without using local storage.
    thanks.

    Reply
  3. i am getting this error please help me:
    [ts] Type ‘Store’ is missing the following properties from type ‘Observable’: _isScalar, source, operator, subscribe, and 5 more. [2740]
    (property) DisplayComponent.coins: Observable

    Reply
  4. i am getting this error please help me:
    [ts] Type ‘Store’ is missing the following properties from type ‘Observable’: _isScalar, source, operator, subscribe, and 5 more. [2740]
    (property) DisplayComponent.coins: Observable

    Reply
    • update the ngrx versions in package json

      “@ngrx/core”: “^1.2.0”,
      “@ngrx/effects”: “9.2.0”,
      “@ngrx/store”: “9.2.0”

      Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.