Angular 6 NgRx Store example – Angular State Management

NgRx Store is a state management solution for Angular apps that helps us build applications by working around our app’s data (state). In this tutorial, we’re gonna look at how to work with NgRx Store, custom Actions, Reducers. Then we will practice to understand all of them in a simple practical Angular 6 example.

Related Posts:
Angular 6 Search Box example with Youtube API & RxJS 6
– NGXS: Angular 6 NGXS example – Angular State Management
– State Management with Redux: Introduction to Redux – a simple practical Redux example

– Reactive Streams:

NgRx Store to manage App State

Why we need a State container

State container helps JavaScript applications to manage state.
=> Whenever we wanna read the state, look into only one single place – NgRx Store.
=> Managing the state could be simplified by dealing with simple objects and pure functions.

NgRx Store

Store holds the current state so that we can see it as a single source of truth for our data.
– access state using store.select(property) (property is defined at app.module.ts in StoreModule.forRoot()).
– update state via store.dispatch(action).

export interface AppState {
    readonly customer: Customer[];
}

// component
import { Store } from '@ngrx/store';
import { AppState } from '../../app.state';

export class MyComponent {

  customers: Observable;

  constructor(private store: Store) {
    this.customers = store.select('customer');
  }

  saveCustomer(data) {
    this.store.dispatch(new ActionCreateCustomer({data}));
  }
}

Action

Action is payload of information that is sent to Store using store.dispatch(action).
Action must have a type property that should typically be defined as string constants. It indicates the type of action being performed:

import { Action } from '@ngrx/store';

export const CREATE_CUSTOMER = 'Customer_Create';
export const DELETE_CUSTOMER = 'Customer_Delete';

export class CreateCustomer implements Action {
    readonly type = CREATE_CUSTOMER;

    constructor(public payload: Customer) { }
}

export class DeleteCustomer implements Action {
    readonly type = DELETE_CUSTOMER;

    constructor(public id: string) { }
}

export type Actions = CreateCustomer | DeleteCustomer;

Reducer

Reducer is a pure function that generates a new state based on an Action it receives. These Actions only describe what happened, but don’t describe how state changes.

export function reducer(
    state: Customer[] = [initialState],
    action: Actions) {

    switch (action.type) {
        case CREATE_CUSTOMER:
            return [...state, action.payload];

        case DELETE_CUSTOMER:
            return state.filter(({ id }) => id !== action.id);

        default:
            return state;
    }
}

*Note: Reducer must be a pure function:
=> From given arguments, just calculate the next state and return it.
=> No side effects. No API or non-pure function calls. No mutations.

Import @ngrx/store and Reducer

app.module.ts

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

@NgModule({
  declarations: [ ... ],
  imports: [
    ...
    StoreModule.forRoot({
      customer: reducer
    })
  ],
  ...
})
export class AppModule { }

Practice

Example overview

angular-6-ngrx-store-example-project-structure

This is a simple Angular 6 with NgRx Store Application that has:
AppState (app.state.ts) as the main state that is stored inside NgRx Store.
– 2 types of Action: CREATE_CUSTOMER and DELETE_CUSTOMER (customer.actions.ts).
– One Reducer (customer.reducer.ts).

We can save/remove Customer. App will update UI immediately.

angular-6-ngrx-store-example-result

>> Click on Delete button from any Customer:

angular-6-ngrx-store-example-delete-result

Step by step

Install NgRx Store

Run cmd: npm install @ngrx/store.

Create Data Model

app/customers/models/customer.ts

export class Customer {
    id: string;
    name: string;
    age: number;
    active: boolean;

    constructor(id?: string, name?: string, age?: number, active?: boolean) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.active = active;
    }
}
Create Actions

app/actions/customer.actions.ts

import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
import { Customer } from '../customers/models/customer';

export const CREATE_CUSTOMER = 'Customer_Create';
export const DELETE_CUSTOMER = 'Customer_Delete';

export class CreateCustomer implements Action {
    readonly type = CREATE_CUSTOMER;

    constructor(public payload: Customer) { }
}

export class DeleteCustomer implements Action {
    readonly type = DELETE_CUSTOMER;

    constructor(public id: string) { }
}

export type Actions = CreateCustomer | DeleteCustomer;
Create Reducer

app/reducers/customer.reducer.ts

import { Customer } from '../customers/models/customer';
import { Actions, CREATE_CUSTOMER, DELETE_CUSTOMER } from '../actions/customer.actions';

const initialState: Customer = {
    id: '1',
    name: 'Andrien',
    age: 27,
    active: true
};

export function reducer(
    state: Customer[] = [initialState],
    action: Actions) {

    switch (action.type) {
        case CREATE_CUSTOMER:
            return [...state, action.payload];

        case DELETE_CUSTOMER:
            return state.filter(({ id }) => id !== action.id);

        default:
            return state;
    }
}
Create App State

app/app.state.ts

import { Customer } from './customers/models/customer';

export interface AppState {
    readonly customer: Customer[];
}
Import NgRx Store

app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

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

import { AppComponent } from './app.component';
import { CreateCustomerComponent } from './customers/create-customer/create-customer.component';
import { CustomersListComponent } from './customers/customers-list/customers-list.component';
import { CustomerDetailsComponent } from './customers/customer-details/customer-details.component';

@NgModule({
  declarations: [
    AppComponent,
    CreateCustomerComponent,
    CustomersListComponent,
    CustomerDetailsComponent
  ],
  imports: [
    BrowserModule,
    StoreModule.forRoot({
      customer: reducer
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Create Components
Create Customer Component

customers/create-customer/create-customer.component.ts

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '../../app.state';
import { CreateCustomer } from '../../actions/customer.actions';

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

  constructor(private store: Store) { }

  ngOnInit() {
  }

  saveCustomer(id, name, age) {
    this.store.dispatch(new CreateCustomer(
      {
        id: id,
        name: name,
        age: age,
        active: false
      }
    ));
  }
}

customers/create-customer/create-customer.component.html

Create Customers

Customer Details Component

customers/customer-details/customer-details.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Customer } from '../models/customer';
import { Store } from '@ngrx/store';
import { AppState } from '../../app.state';
import { DeleteCustomer } from '../../actions/customer.actions';

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

  @Input() customer: Customer;

  constructor(private store: Store) { }

  ngOnInit() {
  }

  removeCustomer(id) {
    this.store.dispatch(new DeleteCustomer(id));
  }
}

customers/customer-details/customer-details.component.html

{{customer.name}}
{{customer.age}}
Delete
Customers List Component

customers/customers-list/customers-list.component.ts

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';

import { Customer } from '../models/customer';
import { AppState } from '../../app.state';

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

  customers: Observable;

  constructor(private store: Store) {
    this.customers = store.select('customer');
  }

  ngOnInit() {
  }

}

customers/customers-list/customers-list.component.html

Customers

Import Components to App Component

app/app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'ozenero';
  description = 'NgRx Example';
}

app/app.component.html

{{title}}

{{description}}

Source Code

Angular6-NgRx-Example

18 thoughts on “Angular 6 NgRx Store example – Angular State Management”

  1. Magnificent beat ! I would like to apprentice while
    you amend your website, how can i subscribe for a blog web site?
    The account helped me a acceptable deal. I had been tiny bit acquainted of
    this your broadcast offered bright clear idea

  2. I just like the valuable information you provide for your articles.
    I’ll bookmark your weblog and test once more right here
    regularly. I’m moderately certain I will be told lots of new stuff right right here!
    Best of luck for the following!

  3. First off I ᴡant to say excellent blog! I had a quick question in which I’d like to ask
    if you don’t mіnd. I was curious to ҝnow how you center yourself and clear your thoughtgs
    before writing. I have had a tough time clearing my thoughts in getting my thoughts out.
    I do enjoy writing but it juѕt seems likee the first 10 to 15 minuttes are
    usually lost jᥙst trying to figuгe out how to begin. Anyy ideas or hints?
    Kudos!

  4. Howdy! I know this is somewhat off topic but I was wondering if you knew where I could locate a captcha plugin for my comment form?
    I’m using the same blog platform as yours and I’m having difficulty finding one?

    Thanks a lot!

  5. Oh my goodness! Impressive article dude! Thanks,
    However I am going through difficulties with your
    RSS. I don’t understand the reason why I am unable
    to subscribe to it. Is there anybody getting similar RSS
    problems? Anybody who knows the solution will
    you kindly respond? Thanks!!

  6. Your style is very unique compared to other people I have
    read stuff from. I appreciate you for posting
    when you’ve got the opportunity, Guess I’ll just book
    mark this page.

  7. Hey just wanted to give you a quick heads up. The text in your content seem to be running off the screen in Opera. I’m not sure if this is a formatting issue or something to do with internet browser compatibility but I figured I’d post to let you know. The design look great though! Hope you get the issue solved soon. Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *