---
title: "State Management in Angular Using NgRx: Pt. 2"
description: "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."
authors:
  - name: "William Juan"
    url: "https://auth0.com/blog/authors/william-juan/"
date: "Nov 23, 2021"
category: "Developers,Tutorial,Angular"
tags: ["angular", "rgnx", "auth"]
url: "https://auth0.com/blog/state-management-in-angular-with-ngrx-2/"
---

# State Management in Angular Using NgRx: Pt. 2

## User State Management

The user store will work similarly to how the menus state management works. I won't detail how each part of the state works and focus more on the Auth0 and NgRx integration.

The starter app is using the Auth0 SDK directly and managing the roles through the `RolesService`, this section of the tutorial will walk you through migrating to using Auth0's SDK through NgRx and managing the roles by using selectors.

### Create User State

Let's start with defining the interface for the user state object and its initial state. Open `user.state.ts` and add the following code 👇

```typescript
// src/app/core/state/user/user.state.ts

import { User as Auth0User } from "@auth0/auth0-spa-js";

export interface UserState {
  userDetails: Auth0User;
}

export const initialState: UserState = {
  userDetails: undefined,
};
```

Create a barrel export for the `user` directory with the following code 👇

```typescript
// src/app/core/state/user/index.ts

export * from "./user.state";
```

#### Update Core State

To access `UserState` from your Store, you will need to add it to your `CoreState`'s state object. Open `core.state.ts` and add the following code 👇

```typescript
// src/app/core/state/core.state.ts

import { MenusState } from "./menus";
// ✨ New 👇
import { UserState } from "./user";

export interface State {
  menus: MenusState;
  // ✨ New 👇
  user: UserState;
}
```

### Create User Actions

We have three user-related Actions that we need for our user Store. Login, logout, and user changed Action to keep the user details in our Store in sync with Auth0's SDK.

Since we have several user Actions that will originate from the Navbar component, you can group them under `allNavbarAction` to ensure you aren't reusing these Actions in a different part of the application (following the Good Action Hygiene pattern).

Because the `userChangedFromAuth0SDK` Action originates from Auth0's SDK, let's name the source part of the Action type as `Auth0 SDK`.

Open `user.actions.ts` and add the following code 👇

```typescript
// src/app/core/state/user/user.actions.ts

import { createAction, props } from "@ngrx/store";
import { User as Auth0User } from "@auth0/auth0-spa-js";

export const allNavbarActions = {
  loginFlowInitiated: createAction("[Navbar] Login Flow Initiated"),
  logoutFlowInitiated: createAction("[Navbar] Logout Flow Initiated"),
};

export const userChangedFromAuth0SDK = createAction(
  "[Auth0 SDK] User Changed",
  props<{ userDetails: Auth0User }>()
);
```

Add `user.actions` to the barrel export. Open `index.ts` and add the following code 👇

```typescript
// src/app/core/state/user/index.ts

export * from "./user.state";

// ✨ New 👇
export * from "./user.actions";
```

#### Update Application to Use User Actions

Open `nav-bar.component.ts` and add the following code 👇

```typescript
// src/app/shared/components/nav-bar/nav-bar.component.ts

import { Component } from "@angular/core";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { faHome, faUser, faUtensils } from "@fortawesome/free-solid-svg-icons";
import { AuthService } from "@auth0/auth0-angular";

// ✨ New 👇
import { Store } from "@ngrx/store";
import { allNavbarActions } from "src/app/core/state/user";

export interface INavBarMenuLinkProps {
  to: string;
  icon: IconDefinition;
  label: string;
}

@Component({
  selector: "app-nav-bar",
  templateUrl: "./nav-bar.component.html",
  styleUrls: ["./nav-bar.component.scss"],
})
export class NavBarComponent {
  faUser = faUser;
  isAuthenticated$ = this.authService.isAuthenticated$;
  user$ = this.authService.user$;

  navOptions: INavBarMenuLinkProps[] = [
    { to: "/home", label: "Home", icon: faHome },
    { to: "/menu", label: "Menu", icon: faUtensils },
  ];

  constructor(
    private authService: AuthService,
    // ✨ New 👇
    private store: Store
  ) {}

  loginWithRedirect(): void {
    // ✨ New 👇
    this.store.dispatch(allNavbarActions.loginFlowInitiated());
  }

  logout(): void {
    // ✨ New 👇
    this.store.dispatch(allNavbarActions.logoutFlowInitiated());
  }
}
```

### Create User Selectors

Create two selectors, a feature selector and a selector that returns the `userDetails`. Open `user.selector.ts` and add the following code 👇

```typescript
// src/app/core/state/user/user.selector.ts

import { createFeatureSelector, createSelector } from "@ngrx/store";
import { UserState } from "./user.state";

export const selectUser = createFeatureSelector<UserState>("user");

export const selectUserDetails = createSelector(
  selectUser,
  (state: UserState) => state.userDetails
);
```

Let's also add some utility Selectors for `isLoggedIn`, `userRoles`, and `isAdmin` to easily let your components access these properties. Open `user.selector.ts` and update it with the following code 👇

```typescript
// src/app/core/state/user/user.selector.ts

import { createFeatureSelector, createSelector } from "@ngrx/store";

// ✨ New 👇
import { environment } from "src/environments/environment";
import { UserState } from "./user.state";

// ✨ New 👇
export const USER_ROLES = {
  MENU_ADMIN: "menu-admin",
};

export const selectUser = createFeatureSelector<UserState>("user");

export const selectUserDetails = createSelector(
  selectUser,
  (state: UserState) => state.userDetails
);

// ✨ New 👇
export const selectIsLoggedIn = createSelector(
  selectUserDetails,
  (userDetails) => !!userDetails
);

// ✨ New 👇
export const selectUserRoles = createSelector(
  selectUserDetails,
  (userDetails) => userDetails[`${environment.auth.audience}/roles`]
);

// ✨ New 👇
export const selectIsAdmin = createSelector(selectUserRoles, (userRoles) =>
  userRoles?.includes(USER_ROLES.MENU_ADMIN)
);
```

Add `user.selector` to the list of exports in `index.ts` 👇

```typescript
// src/app/core/state/user/index.ts

export * from "./user.state";
export * from "./user.actions";

// ✨ New 👇
export * from "./user.selector";
```

#### Update Application to Use User Selectors

The user role route guard was previously using the value of the roles from `RolesService`. Since you now have a user roles Selector, you can replace the current implementation with an NgRx Selector. Open `user-role.guard.ts` and add the following code 👇

```typescript
// src/app/core/guards/user-role.guard.ts

import { Injectable } from "@angular/core";
import {
  CanActivate,
  Router,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
} from "@angular/router";

// ✨ New 👇
import { Store } from "@ngrx/store";

import { Observable, of } from "rxjs";
import { catchError, map } from "rxjs/operators";
import { selectUserRoles } from "../state/user";

@Injectable({ providedIn: "root" })
export class UserRoleGuard implements CanActivate {
  constructor(
    // ✨ New 👇
    private store: Store,
    private router: Router
  ) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> {
    // ✨ New 👇
    return this.store.select(selectUserRoles).pipe(
      map((roles) => {
        if (roles && roles.includes(route?.data?.role)) {
          return true;
        }

        // redirect the user to home
        this.router.navigate(["/home"]);
        return false;
      }),
      catchError((err) => {
        // redirect the user to home
        this.router.navigate(["/home"]);
        return of(false);
      })
    );
  }
}
```

Another place we use user-related information is the Navbar. We display user information and conditionally display the login and logout button depending on the user's `isAuthenticated` state. Open `nav-bar.component.ts` and add the following code 👇

```typescript
// src/app/shared/components/nav-bar/nav-bar.component.ts

import { Component } from "@angular/core";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { faHome, faUser, faUtensils } from "@fortawesome/free-solid-svg-icons";
import { Store } from "@ngrx/store";

// ✨ New 👇
import {
  allNavbarActions,
  selectIsLoggedIn,
  selectUserDetails,
} from "src/app/core/state/user";

export interface INavBarMenuLinkProps {
  to: string;
  icon: IconDefinition;
  label: string;
}

@Component({
  selector: "app-nav-bar",
  templateUrl: "./nav-bar.component.html",
  styleUrls: ["./nav-bar.component.scss"],
})
export class NavBarComponent {
  faUser = faUser;

  // ✨ New 👇
  isAuthenticated$ = this.store.select(selectIsLoggedIn);
  user$ = this.store.select(selectUserDetails);

  navOptions: INavBarMenuLinkProps[] = [
    { to: "/home", label: "Home", icon: faHome },
    { to: "/menu", label: "Menu", icon: faUtensils },
  ];

  constructor(private store: Store) {}

  loginWithRedirect(): void {
    this.store.dispatch(allNavbarActions.loginFlowInitiated());
  }

  logout(): void {
    this.store.dispatch(allNavbarActions.logoutFlowInitiated());
  }
}
```

The profile page displays some information about the user, such as name and photo. Since this is stored as part of the user Store, let's also update this to use the `selectUserDetails` Selector. Open `profile.component.ts` and add the following code 👇

```typescript
// src/app/features/profile/profile.component.ts

import { Component } from "@angular/core";

// ✨ New 👇
import { Store } from "@ngrx/store";
import { selectUserDetails } from "src/app/core/state/user";

@Component({
  selector: "app-profile",
  templateUrl: "./profile.component.html",
  styleUrls: ["./profile.component.scss"],
})
export class ProfileComponent {
  // ✨ New 👇
  user$ = this.store.select(selectUserDetails);

  // ✨ New 👇
  constructor(private store: Store) {}
}
```

We display an `Add` button when the logged-in user is an admin user on the Menu Items page. You can read more about setting up admin users on Auth0 in this [blog post](https://auth0.com/blog/developing-a-secure-api-with-nestjs-adding-role-based-access-control/#Get-User-Roles). Open `menu-items.component.ts` and add the following code 👇

```typescript
// src/app/features/menu/menu-items/menu-items.component.ts

import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Store } from "@ngrx/store";
import { selectMenuItems } from "src/app/core/state/menus";
// ✨ New 👇
import { selectIsAdmin } from "src/app/core/state/user";

@Component({
  selector: "app-menu-items",
  templateUrl: "./menu-items.component.html",
  styles: [
    `
      :host {
        width: 100%;
        height: 100%;
      }
    `,
  ],
})
export class MenuItemsComponent {
  menuItems$ = this.store.select(selectMenuItems);
  // ✨ New 👇
  isAdmin$ = this.store.select(selectIsAdmin);

  constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private store: Store
  ) {}

  addMenuItem(): void {
    this.router.navigate(["add"], { relativeTo: this.activatedRoute });
  }
}
```

Lastly, we display a `Edit` and `Delete` buttons when the logged-in user is an admin user on the Menu Item page. Open `menu-item.component.ts` and add the following code 👇

```typescript
// src/app/features/menu/menu-item/menu-item.component.ts

import { Component } from "@angular/core";
import { Location } from "@angular/common";
import { ActivatedRoute, Router } from "@angular/router";
import { map, switchMap } from "rxjs/operators";
import { Store } from "@ngrx/store";
import { selectMenuItem } from "src/app/core/state/menus";

// ✨ New 👇
import { selectIsAdmin } from "src/app/core/state/user";

@Component({
  selector: "app-menu-item",
  templateUrl: "./menu-item.component.html",
  styleUrls: ["./menu-item.component.scss"],
})
export class MenuItemComponent {
  menuItemId$ = this.activatedRoute.params.pipe(map((params) => params.id));
  menuItem$ = this.menuItemId$.pipe(
    switchMap((id) => this.store.select(selectMenuItem({ id: id })))
  );

  // ✨ New 👇
  isAdmin$ = this.store.select(selectIsAdmin);

  constructor(
    private activatedRoute: ActivatedRoute,
    private router: Router,
    private location: Location,
    private store: Store
  ) {}

  back(): void {
    this.location.back();
  }

  navigateTo(url: string): void {
    this.router.navigateByUrl(`${this.router.url}/${url}`);
  }
}
```

### Create User Reducer

The `userDetails` in the user Store will only be modified based on the `userChanged` event from Auth0's SDK to always keep them in sync. Open `user.reducer.ts` and add the following code 👇

```typescript
// src/app/core/state/user/user.reducer.ts

import { Action, createReducer, on } from "@ngrx/store";
import * as UserActions from "./user.actions";
import { initialState, UserState } from "./user.state";

const userReducer = createReducer(
  initialState,
  on(UserActions.userChangedFromAuth0SDK, (state, { userDetails }) => ({
    ...state,
    userDetails: userDetails,
  }))
);

export function reducer(state: UserState | undefined, action: Action) {
  return userReducer(state, action);
}
```

#### Update Core Reducer

Similar to what you did for the menus Reducer, you will need to add the reducer to the `ActionReducerMap` to use the user reducer. Open `core.reducer.ts` and add the following code 👇

```typescript
// src/app/core/state/core.reducers.ts

import { ActionReducerMap, MetaReducer } from "@ngrx/store";
import { State } from "./core.state";
import * as MenusReducer from "./menus/menus.reducer";
// ✨ New 👇
import * as UserReducer from "./user/user.reducer";

export const reducers: ActionReducerMap<State> = {
  menus: MenusReducer.reducer,
  // ✨ New 👇
  user: UserReducer.reducer,
};

export const metaReducers: MetaReducer<State>[] = [];
```

### Create User Effects

You will be interacting with Auth0's SDK through the user Effects, to log in and log out the user and listen to changes in the user's state. Open `user.effects.ts` and add the following code 👇

```typescript
// src/app/core/state/user/user.effects.ts

import { Injectable } from "@angular/core";
import { AuthService } from "@auth0/auth0-angular";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { tap, map } from "rxjs/operators";
import * as UserActions from "./user.actions";

@Injectable()
export class UserEffects {
  constructor(
    private actions$: Actions<any>,
    private authService: AuthService
  ) {}

  login$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(UserActions.allNavbarActions.loginFlowInitiated.type),
        tap(() => this.authService.loginWithRedirect())
      ),
    { dispatch: false }
  );

  logout$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(UserActions.allNavbarActions.logoutFlowInitiated.type),
        tap(() => this.authService.logout())
      ),
    { dispatch: false }
  );
}
```

To keep the user observable from the Auth0's SDK and our user Store in sync, you can use an actionless effect that listens to an external observable (in our case `authService.user$`) and dispatches another action whenever that observable emits a new value. Open `user.effects.ts` and add the following code 👇

```typescript
// src/app/core/state/user/user.effects.ts

import { Injectable } from "@angular/core";
import { AuthService } from "@auth0/auth0-angular";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { tap, map } from "rxjs/operators";
import * as UserActions from "./user.actions";

@Injectable()
export class UserEffects {
  constructor(
    private actions$: Actions<any>,
    private authService: AuthService
  ) {}

  // ✨ New 👇
  userChanged$ = createEffect(() =>
    this.authService.user$.pipe(
      map((userDetails) =>
        UserActions.userChangedFromAuth0SDK({ userDetails: userDetails })
      )
    )
  );

  login$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(UserActions.allNavbarActions.loginFlowInitiated.type),
        tap(() => this.authService.loginWithRedirect())
      ),
    { dispatch: false }
  );

  logout$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(UserActions.allNavbarActions.logoutFlowInitiated.type),
        tap(() => this.authService.logout())
      ),
    { dispatch: false }
  );
}
```

Add `user.effects` to the barrel export. Open `index.ts` and add the following code 👇

```typescript
// src/app/core/state/user/index.ts

export * from "./user.state";
export * from "./user.actions";
export * from "./user.selector";

// ✨ New 👇
export * from "./user.effects";
```

#### Update Effects Module

You will then need to add `UserEffects` to the `EffectsModule` initialization. Open `app.module.ts` and add the following code

```typescript
// 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 { StoreModule } from "@ngrx/store";
import { EffectsModule } from "@ngrx/effects";
import { AuthHttpInterceptor, AuthModule } from "@auth0/auth0-angular";

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 { MenusEffects } from "./core/state/menus";

// ✨ New 👇
import { UserEffects } from "./core/state/user";

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

    // NgRx
    StoreModule.forRoot(reducers, {
      metaReducers,
    }),

    // ✨ New 👇
    EffectsModule.forRoot([MenusEffects, UserEffects]),
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthHttpInterceptor,
      multi: true,
    },
  ],
})
export class AppModule {}
```

> **Checkpoint:** There aren't any visible functional changes added in this section. The difference between the current state of the app compared to the previous checkpoint is its underlying implementation. The app now uses NgRx versus a `BehaviorSubject` to manage user-related states. The app should display the login button with an empty dashboard when the user is not authenticated, and display the logout button with the logged-in user's name along with the menu items on the dashboard when a user is authenticated. Clicking on the "Log in" and "Log out" buttons should trigger their respective flows using Auth0's SDK and update your application's state. If you open Redux Devtools in your browser, you should see the user state and actions every time you perform any user-related actions.

## Conclusion

State management is a key component when building applications. You added two Stores to our demo application to manage two distinct states - menus and user. This is a relatively small demo application with a few Stores, Actions, and Reducers to show how you can use NgRx to manage your Application's state, and use NgRx with Auth0's SDK to handle user-related functionalities.