Introduction to state management with Ngrx and Angular

Introduction 🚩 :

Hello dear readers and followers, In this Blog (2021 blogging challenge ) I will try to introduce to you the concept of state management pattern with Angular application by explaining the core concepts with full-example code for each one,

What is State Management 📝

Before defining the state management, let’s define what is a State

Types of State 📰

  • Navigation State: where the currentUser and the current route with current params?
  • UI State: a modal is open or not, the button submit disabled or enabled, etc...
  • Application State: the kind of state that can be sued by different parts of the application like the current user is logged or not
  • Network State: if there is a current process of fetching data from APIS
  • Communication state: values that describe requests to other servers (such as a “loading” value)

State Management 📰:

Problem :

Solution :

Defining a way that takes care of defining the global state across all the applications with the operations to manipulate this state ( add, delete, change, etc ..) and facilitate communication and sharing of data across components

Benefits of State Management 🔥 💥 :

Applying the state management pattern to your front-end application can offer a lot of advantages and benefits :

  • Performance (Data flows in one direction) ✔️
  • Applications become easier to test (more organized, State representation is easy to read, data accessible whenever we need it)✔️
  • debugging easier ✔️
  • Automatically sync with local storage. This makes you retain the data whenever you refresh the page ✔️
  • Avoiding a lot of unnecessary APIS calls ✔️

Use cases for state management : 💶 💶

There some common-use cases the requires to apply a state management architecture in our application for example where :

  • Update a page after creating/updating data without refresh
  • Ui global theme changes across the application (Dark mode example )

How Data Flows Without State Management with Angular ❓

Available tooling for State Management with Angular ✒️ ✂️:

There are some popular tools to make state management for our Angular application

Native Rxjs ☝️ :

We can make the data sharing between different parts of our application by using simple Rxjs operators like BehaviorSubject to hold the state and pipeable operators to modify the state.

AKITA ↖️:

NGXS ⚓:

Ngrx ✈️ ✈️ :

What is Ngrx?

Ngrx is a group of Angular libraries for reactive extensions that implements the Redux pattern and it’s supercharged with RXJS

Why we will choose Ngrx ⁉️

Sincerely, Ngrx was the most famous library used for state management, I didn’t tie the other tools to make some deep technical comparison

Ngrx Core-Concepts :

source : https://ngrx.io/guide/store

Store :

State :

The state presents the data structure of the make-ups the store, the fields in the database!

Actions:

Reducers:

Effects:

Selectors :

Cons of Ngrx 🚫 ❌ :

  • the learning curve when you first start working with NgRx.
  • The application will be a bit verbose( introduce several concepts reducers, selectors, effects, new artficats , bundle size!
  • NgRx exponentially increases the code complexity with a hell of a lot of unnecessary boilerplate code

Application Example (counter-app)💻 :

In this part, I will try to explain to you a small application build with angular and Ngrx to master and manipulate some theoretical concepts that we discussed in the other parts

1) Generate an Angular App with Angular-CLI :

In the first step, we will generate a project with angular-CLI

ng new ngrx-poc --style=scss --routing=false

2) Open your project with Vscode :

Just type code. in your terminal with the current project directory

code .

3) Run the App :

Follow these commands to run the project

cd ngrx-pocnpm start

4) Install NgRx and Tools:

We need to install Ngrx dependencies and devtools

npm install @ngrx/store --savenpm install @ngrx/effects --savenpm install @ngrx/store-devtools --save

5) Define your state

The first thing that we will define is the store structure that will be used across our application

export interface CounterState {counter: number;}export const initialState: CounterState = {counter: 4};

6) Define our actions

Next step we will define the different actions that can be applied to our store,

import { createAction, props } from '@ngrx/store';export const increment = createAction('increment');export const decrement = createAction('decrement');export const reset = createAction('reset');export const incrementAction = createAction('[Counter] Increment',props<{ count: number }>());

7) Define our reducer

After defining our store and actions, let’s prepare the reducer the function the element responsible for updating the store

import { Action, createReducer, on, State } from '@ngrx/store';import { CounterState, initialState } from './counter.state';import * as CounterActions from './counter.actions';const counterReducer = createReducer(initialState,on(CounterActions.increment, (state) => {return {...state,counter: state.counter + 2,};}),on(CounterActions.decrement, (state) => {return {...state,counter: state.counter - 2,};}),on(CounterActions.reset, (state) => {return {...state,counter: 0,};}));export function reducer(state: CounterState | undefined, action: Action) {return counterReducer(state, action);}

8) Selectors for store data selection

As we explained in the previous part, the selectors are the part responsible for selecting the necessary data from the state

import { createFeatureSelector, createSelector } from '@ngrx/store';import { CounterState } from './counter.state';export const COUNTER_STATE_NAME = 'counter';export const selectCounter =createFeatureSelector<CounterState>(COUNTER_STATE_NAME);export const getCount = createSelector(selectCounter,(state: CounterState) => state.counter);

9) Add the store and reducer to app. module

We need now the import the StoreModule to the app module and linking the reducer

@NgModule({declarations: [AppComponent],imports: [BrowserModule,StoreModule.forFeature(COUNTER_STATE_NAME,reducer)],providers: [],bootstrap: [AppComponent]})export class AppModule { }

10) Generate counter component and child components

Now, we need to define the UI part of our application, by defining the container component that will be counter-component and the presentation component that will be ( counter-buttons, counter-output )

10.1: Generate the counter component

ng g c counter

10.2: Generate the counter-buttons component

ng g c counter-buttons 
<div><button class="btn btn-primary" (click)="onIncrement()">Increment</button>&nbsp;<button class="btn btn-warning" (click)="onDecrement()">Decrement</button>&nbsp;<button class="btn btn-info" (click)="onReset()">Reset</button></div>
import { Component, OnInit } from '@angular/core';import { Store } from '@ngrx/store';import { decrement, increment, reset } from '../store/counter.actions';import { CounterState } from '../store/counter.state';@Component({selector: 'app-counter-buttons',templateUrl: './counter-buttons.component.html',styleUrls: ['./counter-buttons.component.scss']})export class CounterButtonsComponent implements OnInit {constructor(private store: Store<CounterState>) { }ngOnInit(): void {}onIncrement(){this.store.dispatch(increment())}onDecrement(){this.store.dispatch(decrement())}onReset(){this.store.dispatch(reset())}}

11) component counter-output to output the data

Now we need to define a new component ‘counter-output’ a presentational component to display the current state counter value!

import { Component, OnInit } from '@angular/core';import { Store } from '@ngrx/store';import { Observable } from 'rxjs';import { getCount } from '../store/counter.selector';import { CounterState } from '../store/counter.state';@Component({selector: 'app-counter-output',templateUrl: './counter-output.component.html',styleUrls: ['./counter-output.component.scss']})export class CounterOutputComponent implements OnInit {counter$ : Observable<number>constructor(private store: Store<CounterState>) {}ngOnInit(): void {this.counter$ = this.store.select(getCount)}}
<div><h3>Counter is: {{ counter$ | async }}</h3></div>

12) counter container component final code :

And finally, this is how we should integrate the child components code inside

<div class="row"><div class="col-md-12"><div class="my-6"><app-counter-output></app-counter-output></div><div class="my-6"><app-counter-buttons></app-counter-buttons></div></div></div>
<div><app-counter></app-counter></div>

Run the App:

npm start

Final output :

Appendix 📘 📙 :

Angular ngrx-data :

@ngrx/entity

is a great way to manage immutable collections.

@ngrx/router-store:

@ngrx/schematics

provides scaffolding. NgRx commands get integrated into the Angular CLI, and most of the NgRx elements can be created using angular CLI. So, let’s add NgRx Schematics. (You can use a new terminal window or exit out from the running angular app by pressing the Ctrl+C key )

@ngrx/store-devtools :

Store Devtools provides developer tools and instrumentation for Store.

Example Ngrx devtools screenshot

ngrx-store-logger:

Advanced logging for @ngrx/store applications, ported from redux-logger.

ngrx-store-localstorage:

Nx :

is a set of extensible dev tools for monorepos that extends the Angular CLI. Nx comes with a set of utilities that help writing effects dealing with the router and server communication simpler

References 📓 📕 :

Conclusion ❤️:

For Conclusion, I tried to explain the concept of state management with Angular application pros, cons, and core concepts,

<script>alert('try your best')</script>