Angular NgRx Store is a client-side data management pattern used in large applications. It implements the Redux pattern using Angular’s RxJS observables. The ngrx/store builds on the concepts made famous by Redux and supercharges them with the backing of RxJS.
Redux
Redux is a predictable state container for JavaScript applications. It follows the Unidirectional flow and 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
- Single source of truth.
- The state is read-only.
- Changes are made with pure functions.
Actions
Actions are payloads of information that send data from your application to your store. They contain the information needed to alter your store. An 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. This library helps you handle events in your application by providing observables and many operators to transform incoming data. It is a component or service that reacts to data modifications.
Here is the step-by-step guide to implementing the ngrx store in Angular 18.
Step 1: Setup Angular 18
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
Step 2: Install Bootstrap 5
Type the following command to install Bootstrap 5 CSS Framework.
npm install bootstrap
Register this CSS framework in the src/angular.json file:
"styles": [ "src/styles.css", "node_modules/bootstrap/dist/css/bootstrap.min.css" ],
Step 3: Define a model
We need to define our application’s schema, so create a file called blockchain.model.ts inside the blockchain folder.
// blockchain.model.ts export interface Blockchain { name: string; price: number; }
I have defined the schema of our application.
Step 4: Install the ngrx libraries.
npm install @ngrx/store npm install @ngrx/effects npm install @ngrx/store-devtools
Step 5: 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. Add the below code in it.
// blockchain.reducer.ts import { Blockchain } from '../blockchain/blockchain.model'; import { createReducer, on } from '@ngrx/store'; import { createAction, props } from '@ngrx/store'; export const ADD_COIN = '[Blockchain] Add Coin'; export const addCoin = createAction(ADD_COIN, props<{ coin: Blockchain }>()); export const initialState: Blockchain[] = []; const _blockchainReducer = createReducer( initialState, on(addCoin, (state, { coin }) => [...state, coin]) ); export function blockchainReducer(state: any, action: any) { return _blockchainReducer(state, action); }
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 application’s old state.
Step 6: Configuring the ngrx store in our application
Go to the src/app/app.config.ts file and add the below code in it:
// app.config.ts import { ApplicationConfig, provideZoneChangeDetection, isDevMode, } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; import { provideStore } from '@ngrx/store'; import { provideEffects } from '@ngrx/effects'; import { provideStoreDevtools } from '@ngrx/store-devtools'; import { blockchainReducer } from './reducers/blockchain.reducer'; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideStore({ blockchain: blockchainReducer }), provideEffects([]), provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }), ], };
I have passed the reducer to the store now. If we need to change our application, we must state the dispatch of the actions, change the state, and update the store.
Step 7: Adding 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 (ngSubmit)="onSubmit($event, name.value, price.value)"> <div class="form-group"> <label class="col-md-4">Coin Name</label> <input type="text" class="form-control" #name required /> </div> <div class="form-group"> <label class="col-md-4">Coin Price</label> <input type="number" class="form-control" #price required /> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Add</button> </div> </form> </div> </div>
Write the onSubmit() function into the blockchain.component.ts file.
// blockchain.component.ts import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from './../app.state'; import { addCoin } from '../reducers/blockchain.reducer'; @Component({ selector: 'app-blockchain', templateUrl: './blockchain.component.html', standalone: true, styleUrls: ['./blockchain.component.css'], }) export class BlockchainComponent implements OnInit { constructor(private store: Store<AppState>) {} onSubmit(event: Event, name: string, price: string) { event.preventDefault(); const coinPrice = parseFloat(price); if (!isNaN(coinPrice)) { this.store.dispatch(addCoin({ coin: { name, price: coinPrice } })); } else { console.error('Invalid price value'); } } ngOnInit(): void {} }
The next step will be to implement the Global AppState in our application.
Step 8: Define the AppState.
In the app folder, create one file called app.state.ts and add the below code:
// app.state.ts import { Blockchain } from './blockchain/blockchain.model'; export interface AppState { readonly blockchain: Blockchain[]; }
Step 9: Create a view component to display data
Go to the terminal and hit the following command.
ng g c display --standalone
Subscribe to the store and fetch the data. So write the following code into the display.component.ts file.
// display.component.ts import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from './../app.state'; import { Blockchain } from './../blockchain/blockchain.model'; import { Observable } from 'rxjs'; import { CommonModule } from '@angular/common'; @Component({ selector: 'app-display', standalone: true, templateUrl: './display.component.html', imports: [CommonModule], 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(): void {} }
Write the HTML view for this component. Add the below code inside src/app/display/display.component.html file:
<table class="table table-hover"> <thead> <tr> <th>Coin Name</th> <th>Coin Price</th> </tr> </thead> <tbody> <tr *ngFor="let coin of coins$ | async"> <td>{{ coin.name }}</td> <td>{{ coin.price }}</td> </tr> </tbody> </table>
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>
And modify our final src/app/app.component.ts file:
// app.component.ts import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { BlockchainComponent } from './blockchain/blockchain.component'; import { DisplayComponent } from './display/display.component'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet, BlockchainComponent, DisplayComponent], templateUrl: './app.component.html', styleUrl: './app.component.css', }) export class AppComponent { title = 'ngrxstore'; }
Add the Coin name and price; you will see it will be stored in the ngrx/store.
Fetch that data from the store and display that information.
That’s all!
AK
Nice Explanation! Thank You.
valera
great work!
ahmed
thanks good explaination
Alejandro
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
sureshh
Hi, i have added removeCoin support. Unfortunately i could not upload code to this page and so code is uploaded at: https://gist.github.com/sureshhatgithub/f0d8fc4874d97467cfbcffae5dac6885
Hope this helps.
Shobhit Singh
Nice explanation,
This tutorial cleared all my doubts related to ngrx
Thanks
PCD Pharma
Awesome explanation. Very helpful.
Thank you.
Arjun Panicker
Very simple and awesome explanation.
Thanks a lot for this.
shashi ranjan tiwari
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.
priya
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
gopriya
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
Mayank
update the ngrx versions in package json
“@ngrx/core”: “^1.2.0”,
“@ngrx/effects”: “9.2.0”,
“@ngrx/store”: “9.2.0”
Min Thu
Very Nice Explanation
Theeban
really effective for me..
Thank u sir.. 🙂
Necqui
I have updated the project to Angular11.
I have also added the removeCoin support as suggested by Sureshh.
The code can be downloaded from
https://github.com/Necqui/NgRxStore