When working on an Angular application, there is no out-of-the-box, quick solution for data persistence. As a result, the experience of developing a very large application that requires a lot of data communication between components can be very stressful. Show In this Angular state management tutorial, we’ll show you how to use NgRx, a collection of libraries for reactive extensions, to manage state in your Angular application. Here’s what we’ll cover:
To follow along with this tutorial, you should have:
Managing state in frontend applicationsUnlike backend applications, which use databases for state management, frontend applications need some sort of mechanism for handling data. This data can range from server responses gotten from HTTP request, to form input data and user routes. It’s a good practice to compose all your application state in a central store for easy management and data communication. So, basically, our state is a representation of our application that actually lives in the store. What is NgRx?NgRx is a group of Angular libraries for reactive extensions and state management. It makes Angular development easier by simplifying the application’s state in objects and enforcing unidirectional data flow. A complete state management system should enable you to model a state — e.g., create a simple representation of what the state should look like, update its value, monitor the state when the value changes, and retrieve the values of the state. Libraries included in the NgRx package include:
For this tutorial, we’ll mostly focus on the Store library. Specifically, we’ll demonstrate how Angular components can get values from a single store. What is NgRx Store?The NgRx Store is a Redux-inspired state management system that enables you to use observables to manage state in an Angular application. The primary advantage to using the NgRx Store is the ability to store all state in a single tree that is accessible from any part of the application. NgRx and ReduxNgRx uses the Redux pattern, which is comprised of three main concepts:
The diagram above clearly illustrates the concept of Redux and shows how it manages state. Note that NgRx also uses this pattern. Let’s say, for instance, we have a button in a particular component that, when clicked, changes the value of the page header. We’ll use NgRx to handle that. First, the component dispatches an action. The action then goes to the reducer. The reducer is a simple method that takes the current state and the action as a parameter and then returns a new state. When the reducer returns the new state, the component then subscribes to a selector to get the new value. The state is never changed directly. Instead, the reducer always creates a new state. This is known as immutability. Using store states, actions, and reducers in NgRxIn this section, we’ll demonstrate how to use store states, actions, and reducers to simplify state management in Angular. Store stateA store is basically a JavaScript object that holds data we’ll be using in our application. A simple store takes the following format: const state = { persons: [ { name: "Wisdom Ekpot", age: 10 }, { name: "John Cat", age: 40 }, { name: "James John", age: 11 } ], bookDescription: { name: "Name of book", author: "Wisdom Ekpot" } } ActionsActions are methods dispatched by the component when an event is called. Here you can define a type and also the payload it will be sending: const ChangeNameAction = { type: "Change Name", name: "Wisdom" } ReducersThe
To access the state, we have to import the initial state into the reducer file. To trigger an action, we use the const _reducer = createReducer( initialState, on(nameOfAction, (state, action) => { return { ...state, someState: action.anyState } }) ) Building a simple Angular app using NgRx StoreStart by setting up a new Angular application. If you don’t have Angular installed on your local machine, you can open up your terminal and
run To create a new project, run Next, install the NgRx Store into your project by running the following command in the terminal (make sure your terminal is on the project working directory): npm install @ngrx/store --save After installation, open up your application on VS Code and serve the application using the following command: code . && ng serve Create a Inside the export interface CourseItem { id: string; department: string; name: string; } Next, create an More great articles from LogRocket:
Create a import { Action } from '@ngrx/store'; import { CourseItem } from '../models/courseItem.model'; export enum CourseActionType { ADD_ITEM = '[COURSE] Add Course', } export class AddItemAction implements Action { readonly type = CourseActionType.ADD_ITEM; //add an optional payload constructor(public payload: CourseItem) {} } export type CourseAction = AddItemAction; We start by importing the NgRx Action from the store and then also import the We’ll use the TypeScript enum to define an action that allows us to declare a set of named constants, such as a collection of related numeric or string values. After doing this, we create an Next, we’ll export a Let’s create a // import the interface import { CourseItem } from '../models/courseItem.model'; import { CourseAction, CourseActionType } from '../actions/course.action'; //create a dummy initial state const initialState: Array<CourseItem> = [ { id: '1', department: 'Computer Engineering', name: 'C++ Programming', }, ]; export function courseReducer( state: Array<CourseItem> = initialState, action: CourseAction ) { switch (action.type) { case CourseActionType.ADD_ITEM: return [...state, action.payload]; default: return state; } } The first thing to do is import the We create a reducer function that takes a Recall that the objective of NgRx or any state management system is to keep all application state in a single store so that it can be assessable from any part of the application. Let’s create a Add the following to the file you just created: import { CourseItem } from './courseItem.model'; export interface State { readonly courses: Array<CourseItem>; } Now we have
to register NgRx in our root import { CourseReducer } from './store/reducers/course.reducer'; import { FormsModule } from '@angular/forms'; After importing the module, register it in the imports: [ FormsModule, StoreModule.forRoot({ course: CourseReducer, }), ], We can now use NgRx in our components. Let’s start by modifying our root import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { CourseItem } from './store/models/courseItem.model'; import { AppState } from './store/models/app-state.model'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit { courseItems$: Observable<Array<CourseItem>>; constructor(private store: Store<AppState>) {} ngOnInit(): void { this.courseItems$ = this.store.select((store) => store.course); } } We bring in
RxJS Observables, our defined We can now use this in our template. We’ll delete all of our template HTML in <h4>Testing NgRx</h4> <ul> <li *ngFor="let course of courseItems$ | async"> {{course.department}} </li> </ul> We can use This is returned because we haven’t called any reducer, so it just returns the state. Adding a courseThe next functionality we’ll tackle is adding a course to our list of courses. We’ll create a simple form for users to add a new course. For a nice-looking user interface, we’ll use
Bootstrap. We’ll add the Bootstrap CSS CDN to our <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>CourseList</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous"> </head> <body> <app-root></app-root> </body> </html> To create a simple form for adding the course name and department in our user interface, let’s modify our <section> <div class="container"> <div class="row"> <div class="col-md-12"> <h4>Angular State Management</h4> </div> <div class="col-md-6"> <ul class="list-group"> <li class="list-group-item" *ngFor="let course of courseItems$ | async"> {{course.name}}: <b>{{course.department}}</b> </li> </ul> </div> <div class="col-md-6"> <div class="card p-4 shadow-sm"> <form #myform="ngForm" (ngSubmit)="addCourse(myform)"> <div class="form-group"> <label for="name">Identity</label> <input type="text" class="form-control" ngModel name="id" id="id" aria-describedby="identity" required> </div> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" ngModel name="name" id="name" aria-describedby="name"> </div> <div class="form-group"> <label for="department">Department</label> <input type="text" class="form-control" ngModel name="department" id="department"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </div> </section> This is a basic code refactor. Here, we’re using the Angular form ngModel to get the values of the form.
Next, we need to import import { NgForm } from '@angular/forms'; import { AddItemAction } from './store/actions/course.action'; //create the method for adding a new course and then reset the form addCourse(form: NgForm) { this.store.dispatch(new AddItemAction(form.value)); form.reset(); } We can now add a new course using the form we created: ConclusionWorking with large-scale applications requires good architecture and organized structure. State management tools such as NgRx can help you maintain a readable codebase as your app scales. Whether you’re an Angular developer or not, understanding the basic concepts behind state management will make it easier to use tools such as Redux and Vuex in your projects. The source code for this article is available on GitHub. Experience your Angular apps exactly how a user doesDebugging Angular applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Angular state and actions for all of your users in production, try LogRocket. LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your site including network requests, JavaScript errors, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. The LogRocket NgRx plugin logs Angular state and actions to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred. Modernize how you debug your Angular apps - Start monitoring for free. |