Installing NgRx page

Learn how to add NgRx to an Angular project.

Quick Start: You can checkout this branch to get your codebase ready to work on this section.

Overview

  1. Add NgRx schematics.

  2. Add NgRx dependencies.

  3. Generate Global Store using NgRx schematics.

  4. Generate Login Feature Store using NgRx schematics.

Installing Project Dependencies

We will need access to the Angular cli for this section, so if you haven’t already installed the project’s dependencies (Getting Started), be sure to install them:

npm install

NgRx Dependencies

Since we are going to use multiple NgRx tools, let’s install everything we need:

NgRx Schematics

First, install NgRx schematics, a tool that will allow us to run schematics from the CLI to quickly generate code.

This command will install NgRx schematics and update angular.json:

npx ng add @ngrx/schematics@14

NgRx Libraries

Next, let’s install the NgRx dependencies.

This command will install the NgRx dependencies and update package.json and package-lock.json:

npm install @ngrx/{store,effects,entity,store-devtools}@14 --save

Generating Global Store

We’ll take advantage of NgRx schematics to generate our initial state management files, and register the root of our Global Store within app.module.ts.

npx ng generate store State --root --state-path store --module app.module.ts

We are now setup to be able to generate NgRx Features.

Generating Login Feature Set

The Login Feature Set in our application will be responsible for holding information about authentication and the authenticated user.

Setup

We’ll take advantage of NgRx schematics to quickly generate a Feature Set:

npx ng generate feature store/login/Login --module app.module.ts --reducers ../../store/index.ts

NgRx schematics will prompt us with a few questions:

  • Should we generate and wire success and failure actions? Yes. We will modify the Actions a little bit, but this is enough to get started.
  • What should be the prefix of the action, effect and reducer? load. The default value. We will change the names for Actions, Effects and Reducers, so don’t worry about the prefix right now.

This command accomplishes the following:

  1. Creates a src/app/store/login directory containing login.actions.ts, login.effects.ts, login.reducer.ts, and login.selecors.ts, as well as associated spec files
  2. Updates app.module.ts to initialize the Login Feature Store and Feature Effects
  3. Updates src/app/store/index.ts to: a. Add Login Feature Reducers map b. Add the Login State type to the Global State interface

Register Root EffectsModule in AppModule

This project has prettier installed, so you can format files throughout the course. Right now the imports for AppModule found at src/app/app.module.ts has a long imports array. To make this more readable, we will format this file. To do this using vscode, we can open src/app/app.module.ts then press Shift + Option + F for Mac or press Shift + Alt + F for Windows:

src/app/app.module.ts

// src/app/app.module.ts

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginModule } from './login/login.module';
import { StoreModule } from '@ngrx/store';
import { reducers, metaReducers } from './store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import * as fromLogin from './store/login/login.reducer';
import { EffectsModule } from '@ngrx/effects';
import { LoginEffects } from './store/login/login.effects';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    LoginModule,
    StoreModule.forRoot(reducers, { metaReducers }),
    !environment.production ? StoreDevtoolsModule.instrument() : [],
    StoreModule.forFeature(fromLogin.loginFeatureKey, fromLogin.reducer),
    EffectsModule.forFeature([LoginEffects]),
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Next, we need to manually update our app.module.ts to register NgRx Global Effects. To accomplish this, we need to add EffectsModuloe.forRoot([]) to our AppModule imports, as shown below:

src/app/app.module.ts

// src/app/app.module.ts

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginModule } from './login/login.module';
import { StoreModule } from '@ngrx/store';
import { reducers, metaReducers } from './store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import * as fromLogin from './store/login/login.reducer';
import { EffectsModule } from '@ngrx/effects';
import { LoginEffects } from './store/login/login.effects';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    LoginModule,
    StoreModule.forRoot(reducers, { metaReducers }),
    EffectsModule.forRoot([]),
    !environment.production ? StoreDevtoolsModule.instrument() : [],
    StoreModule.forFeature(fromLogin.loginFeatureKey, fromLogin.reducer),
    EffectsModule.forFeature([LoginEffects]),
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Generate Login Feature

Lastly, we need to update src/app/store/login/login.reducer.ts to include a LoginPartialState interface, which is easier to import when:

  1. Writing tests for Components using NgRx Selectors
  2. Writing tests for NgRx Selectors themselves
src/app/store/login/login.reducer.ts
// src/app/store/login/login.reducer.ts

import { Action, createReducer, on } from '@ngrx/store';
import * as LoginActions from './login.actions';

export const loginFeatureKey = 'login';

export interface State {

}

export interface LoginPartialState {
  [loginFeatureKey]: State;
}

export const initialState: State = {

};

export const reducer = createReducer(
  initialState,

  on(LoginActions.loadLogins, state => state),
  on(LoginActions.loadLoginsSuccess, (state, action) => state),
  on(LoginActions.loadLoginsFailure, (state, action) => state),

);

Wrap-up: By the end of this section, your code should match this branch. You can also compare the code changes for our solution to this section on GitHub or you can use the following command in your terminal:

git diff origin/ngrx-init