DEV Community

Robertino
Robertino

Posted on

🛠 State Management in Angular Using NgRx

📕 See how it's easy to manage your Angular application's state using NgRx and learn how you can use NgRx with Auth0's SDK to handle user-related functionalities.


Intro

State management is a key component when building applications. There are various approaches by which we can manage the state in an Angular application, each with its pros and cons.
This blog post will focus on using NgRx as our state management solution. We will look at how you can use NgRx to manage your application's state by building a Recipe Admin Dashboard application. We will also learn how to secure the application using Auth0 and how it works with NgRx.

What Is Ngrx

NgRx is a framework for building reactive applications in Angular. NgRx is inspired by the Redux pattern - unifying the events in your application and deriving state using RxJS. At a high level, NgRx stores a single state and uses actions to express state changes. NgRx excels in managing complex states, making it ideal for applications with a lot of user interactions and multiple data sources.

How Does Ngrx Work

NgRx is made up of 5 main components - Store, Actions, Reducers, Selectors, and Effects.

NgRx uses the Redux concept of unidirectional data flow, where all application data goes through the same lifecycle. This unidirectional data flow makes the application's state more predictable and thus easier to understand. This flow only applies to the state management layer and is not to be confused with the unidirectional data flow of the presentation layer. The following diagram shows the state management lifecycle in NgRx.

NgRx State Management Lifecycle Diagram

Store

You can think of this as a client-side database. The Store in NgRx acts as the application's single source of truth. It reflects the current state of the app.

Actions

Actions express unique events that happen in our application. These events range from application lifecycle events, user interactions, to network requests. Actions are how the application communicates with NgRx to tell it what to do.

Reducers

Reducers are responsible for handling transitions between states. Reducers react to the Actions dispatched and executes a pure function to update the Store. Pure functions are functions that are predictable and have no side effects. Given the same set of inputs, a pure function will always return the same set of outputs.

Selectors

Selectors are pure functions for getting slices of the state from the Store. Selectors are how our application can listen to state changes.

Effects

Effects handle the side effects of each Action. These side effects range from communicating with an external API via HTTP when a certain Action is dispatched to dispatching another Action to update another part of the State.

Prerequisites

Angular requires an active LTS or maintenance LTS version of Node.js. Angular applications also depend on npm packages for many features and functions. To download and install npm packages, you need an npm package manager such as npm or yarn.

This project has a server-side component to it that has to run in parallel when running the Frontend. Follow the instructions in the Api Express Typescript Menu repo. You can read more about setting up the server-side with Auth0 in this blog post.

This tutorial focuses on how to use Auth0 with NgRx. For more information on setting up Auth0 for Angular applications, follow the instructions on the README or refer to this blog post.

Getting Started Quickly

I created a demo application with the basic structure and components to help you implement the NgRx related part.

Clone the demo app and check out its starter branch:

git clone -b starter git@github.com:auth0-developer-hub/spa_angular_typescript_dashboard.git
Enter fullscreen mode Exit fullscreen mode

Once you clone the repo, make spa_angular_typescript_dashboard your current directory:

cd spa_angular_typescript_dashboard
Enter fullscreen mode Exit fullscreen mode

Install the project's dependencies:

npm i
Enter fullscreen mode Exit fullscreen mode

Run the project locally:

npm run start
Enter fullscreen mode Exit fullscreen mode

Devtools

You can use the redux devtools extension for Chrome or Firefox for debugging store-related operations.

To use this extension with NgRx, you'll need to add NgRx's devtools dependency to our project. You can do this using either npm or Angular's CLI.

Using npm

npm install @ngrx/store-devtools --save
Enter fullscreen mode Exit fullscreen mode

Using Angular's CLI

To use the Angular CLI, you will need to have the CLI installed globally. Refer to instructions on the Angular docs for how to set this up.

ng add @ngrx/store-devtools@latest
Enter fullscreen mode Exit fullscreen mode

Import the StoreDevToolsModule in our AppModule and configure it based on your project's requirements. For this tutorial, you'll be using their default configuration. Open app.module.ts and add the following code 👇

// src/app/app.module.ts

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
import { AuthHttpInterceptor, AuthModule } from "@auth0/auth0-angular";

// ✨ New 👇
import { StoreDevtoolsModule } from "@ngrx/store-devtools";

import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { NavBarModule } from "./shared";
import { environment } from "src/environments/environment";
import { reducers, metaReducers } from "./core/state";
import { UserEffects } from "./core/state/user";
import { MenusEffects } from "./core/state/menus";

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    AuthModule.forRoot({
      ...environment.auth,
      httpInterceptor: {
        allowedList: [
          `${environment.serverUrl}/api/menu/items`,
          `${environment.serverUrl}/api/menu/items/*`,
        ],
      },
    }),
    AppRoutingModule,
    NavBarModule,

    // ✨ New 👇
    StoreDevtoolsModule.instrument({
      maxAge: 25,
      logOnly: environment.production,
    }),
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthHttpInterceptor,
      multi: true,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

If you used Angular's CLI to add NgRx devtools, this part might have been updated automatically.

Running the App and turning on Devtools

After following the steps in this section, you should see an option to activate Redux Devtools in your toolbar when you run the app. Once activated, you should see a window with an interface similar to the image below.

Redux Devtools with NgRx

You can learn more about these features from their official documentation.

Install NgRx

You can either use npm or Angular's CLI to install NgRx dependencies.

Using npm

npm install @ngrx/store --save
Enter fullscreen mode Exit fullscreen mode

Using Angular's CLI

ng add @ngrx/store@latest
Enter fullscreen mode Exit fullscreen mode

You will also be using NgRx's Effects to handle your store's side effects. Let's add that dependency.

Using npm

npm install @ngrx/effects --save
Enter fullscreen mode Exit fullscreen mode

Using Angular's CLI

ng add @ngrx/effects@latest
Enter fullscreen mode Exit fullscreen mode

At the time this post was written, the latest NgRx store and effects version was 12.2.0, which will be the version we will be using throughout the tutorial.

Architecture

At its core, the app is an admin dashboard where the logged-in user can add, edit, and delete a menu item. The logged-in user will perform all the operations or a subset of it depending on their permissions. You will use NgRx to manage the state updates and user/server related events.

You will create two Stores for our application:

  • menus: to manage menu related functionalities (CRUD operations)
  • user: to manage user-related functionalities such as authentication using Auth0

You will also split each part of the Store into individual files to make it easier to follow throughout the tutorial.

Let's first start by creating the following folder structure in our core directory (you will be updating each file as we discuss each section):

|- src/app/core
    |- state
        |- menus
            |- menus.actions.ts
            |- menus.effects.ts
            |- menus.reducer.ts
            |- menus.selector.ts
            |- menus.state.ts
            |- index.ts
        |- user
            |- user.actions.ts
            |- user.effects.ts
            |- user.reducer.ts
            |- user.selector.ts
            |- user.state.ts
            |- index.ts
        |- core.state.ts
        |- core.reducer.ts
        |- index.ts
Enter fullscreen mode Exit fullscreen mode

The starter app is using a BehaviorSubject in menu-state.service.ts to manage it's state. This tutorial will walk you through migrating that to NgRx.

Menus State Management

Create Menus State

Let's start with creating the state object for menus. Open menus.state.ts and add the following code 👇
Read more...

Top comments (0)