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
ng new ngrxDemo
This will create ngrxDemo angular repository.
Step:2 – Install ngrx schematics
Install ngrx schematics package by running the command
npm 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
ng 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
npm 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
. ng generate store State –root –statePath store/reducers –module app.module.ts
This command also imports the store module in app.module.ts
import { StoreModule } from ‘@ngrx/store’;
import { reducers, metaReducers } from ‘./store/reducers’;
import { StoreDevtoolsModule } from ‘@ngrx/store-devtools’;
import { environment } from ‘../environments/environment’;
imports: [
BrowserModule,
StoreModule.forRoot(reducers, { metaReducers }),
!environment.production ? StoreDevtoolsModule.instrument() : []
],
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
ng generate action store/actions/user
This will create user.actions.ts in store/actions folder. .src/app/store/actions/user.actions.ts
import { Action } from ‘@ngrx/store’;
export enum UserActionTypes {
LoadUsers = ‘[User] Load Users’,
}
export class LoadUsers implements Action {
readonly type = UserActionTypes.LoadUsers;
}
export 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
ng 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.
.src/app/store/reducers/user.reducer.ts
import { Action } from ‘@ngrx/store’;
export interface State { }
export const initialState: State = {};
export function reducer(state = initialState, action: Action): State {
switch (action.type) {
default:
return state;
}
}
This reducer is added to the main reducer.
.src/app/store/reducers.index.ts
import * as fromUser from ‘./user.reducer’;
export interface State {
user: fromUser.State;
}
export const reducers: ActionReducerMap<State> = {
user: fromUser.reducer,
};
Step:8 – Create app service
In this step we are creating the service to get the data from users.json
.src/assets/data/users.json
[{
"id": 1,
"userName": "test1",
"email": "test1@gmail.com"
},{
"id": 2,
"userName": "test2",
"email": "test@gmail.com"
},{
"id": 3,
"userName": "test3",
"email": "test3@gmail.com"
},{
"id": 4,
"userName": "test4",
"email": "test4@gmail.com"
}]
Creating the service by running the command
ng 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.
.src/app/services/app.service.ts
import { Injectable } from ‘@angular/core’;
import { HttpClient } from ‘@angular/common/http’;
@Injectable({
providedIn: ‘root’
})
export class AppService {
constructor(private http: HttpClient) { }
getUser() {
return this.http.get(‘assets/data/users.json’);
}
}
Import the HttpClientModule in app.module.ts
import { HttpClientModule } from ‘@angular/common/http’;
imports: [
BrowserModule,
StoreModule.forRoot(reducers, { metaReducers }),
!environment.production ? StoreDevtoolsModule.instrument() : [],
HttpClientModule
],
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
.src/app/store/actions/user.actions.ts
export class LoadUsers implements Action {
constructor(
public users: any
) { }
readonly type = UserActionTypes.LoadUsers;
}
Add users state in the user reducer to store the data to the store.
.src/app/store/reducers/user.reducer.ts
import { UserActions, UserActionTypes } from ‘../actions/user.actions’;
export interface State {
users: any;
}
export const initialState: State = {
users: []
};
export function reducer(state = initialState, action: UserActions): State {
switch (action.type) {
case UserActionTypes.LoadUsers:
return { …state, users: action.users };
default:
return state;
}
}
Import the store in app component to dispatch the users data to store.
.src/app/app.component.ts
import { Component, OnInit } from ‘@angular/core’;
import { AppService } from ‘./services/app.service’;
import { Store } from ‘@ngrx/store’;
import * as fromStore from ‘./store/reducers/index’;
import * as UserActions from ‘./store/actions/user.actions’;
@Component({
selector: ‘app-root’,
templateUrl: ‘./app.component.html’,
styleUrls: [‘./app.component.css’]
})
export class AppComponent implements OnInit {
constructor(
private appService: AppService,
public store: Store<fromStore.State>
) { }
ngOnInit() {
this.appService.getUser().subscribe(res => {
this.store.dispatch(new UserActions.LoadUsers(res));
});
}
}
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
ng generate component /components/userComponent
This will create user-component in components folder
.src/app/components/user-component/user-component.component.html
<p>
user-component works!
</p>
.src/app/components/user-component/user-component.component.ts
import { Component, OnInit } from ‘@angular/core’;
@Component({
selector: ‘app-user-component’,
templateUrl: ‘./user-component.component.html’,
styleUrls: [‘./user-component.component.css’]
})
export class UserComponentComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
Step:11 – Select the users data from the store
Import the store in the user component to get the users data from the store.
.src/app/components/user-component/user-component.component.ts
import { Store } from ‘@ngrx/store’;
import * as fromStore from ‘../../store/reducers/index’;
public users = [];
constructor(public store: Store < fromStore.State >) { }
ngOnInit() {
this.store.select(‘user’).subscribe(res => {
this.users = res.users;
})
}
.src/app/components/user-component/user-component.component.html
<div>
<ul>
<li *ngFor="let user of users">
<p>Username: {{user.userName}}</p>
<p>Email: {{user.email}}</p>
</li>
</ul>
</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
.src/app/store/reducers/user.reducer.ts
export const getUsers = (state: State) => state.users;
Import the selector in main reducer
.src/app/store/reducers/index.ts
import {
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from ‘@ngrx/store’;
export const selectUserState = createFeatureSelector < fromUser.State > (‘user’);
export 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
.src/app/components/user-component/user-component.component.ts
getUsersFromSelectors() {
this.store.select(fromStore.getUserName).subscribe(res => {
console.log(res);
});
}