NgRx Course – Angular NgRx Setup

In the last video, we set up json-server, we created the modules and the components for our application. We also added the initial styles and the HTML for the components.

In this part, we’re going to set up NgRx. First, we’re going to install all the libraries we’ll need with the following command:

$ npm install @ngrx/store @ngrx/effects @ngrx/router-store @ngrx/entity  @ngrx/store-devtools

Now that we have all the libraries installed we can start creating our first actions and reducers.

We already created the customer.model.ts file in the previous video with the following interface:

// customers/customer.model.ts
export interface Customer {
  id?: number;
  name: string;
  phone: string;
  address: string;
  membership: string;
}

Now, we’re going to create a folder for all the files related to the customer state. We’re going to give it the name state and inside we’re going to create 2 files: customer.actions.ts and customer.reducer.ts.

But before we start writing the code, let’s review the flow for a second. When the user interacts with the template, the component dispatches an action, specifying the type of the action and the payload. This is an example of an action:

{
  type: ADD_CUSTOMER,
  payload: {
    name: ‘John Doe’,
  }
}

The reducer that is listening for that action will take the slice of state and the action being dispatched as arguments. Depending on the type of the action, the reducer will return a new state with the changes:

function reducer(state, action) {
  switch(action.type) {
    case ADD_CUSTOMER:
      return {
        ...state,
        action.payload
      }
    }
    default:
      return state;
}
The state in the store will then be replaced with the new state:

...
customers: [
  {
    name: “John Doe”
  }
]
…

So, let’s create a basic example in our application to see this clearly. First, we’re going to set up the store and the reducer for the customer module, as we’re organizing our application by features:

Initialize the root application state in the app.module.ts:

// app.module.ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

// NgRx
import { StoreModule } from "@ngrx/store";

import { AppComponent } from "./app.component";
import { HomeComponent } from "./home/home.component";
import { NavbarComponent } from "./navbar/navbar.component";
import { AppRoutingModule } from "./app-routing.module";

@NgModule({
  declarations: [AppComponent, HomeComponent, NavbarComponent],
  imports: [
    BrowserModule,
    // initialize the root application state
    StoreModule.forRoot({}),
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

export class AppModule {}

In the StoreModule.forRoot() method, we could pass in a reducer for the root state in case we need it. But for now, we’ll don’t register any reducer here. Instead, we’ll do that in the customer.module.ts file:

// customers/customer.module.ts
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { RouterModule, Routes } from "@angular/router";

/* NgRx */
import { StoreModule } from "@ngrx/store";
import { customerReducer } from "./state/customer.reducer";

import { CustomerComponent } from "./customer/customer.component";
import { CustomerAddComponent } from "./customer-add/customer-add.component";
import { CustomerEditComponent } from "./customer-edit/customer-edit.component";
import { CustomerListComponent } from "./customer-list/customer-list.component";

// Routes
const customerRoutes: Routes = [{ path: "", component: CustomerComponent }];

@NgModule({
  imports: [
    CommonModule, 
    RouterModule.forChild(customerRoutes),
    StoreModule.forFeature("customers", customerReducer),
  ],
  declarations: [
    CustomerComponent,
    CustomerAddComponent,
    CustomerEditComponent,
    CustomerListComponent
  ]
})

export class CustomersModule {}

As you can see, we use the StoreModule.forFeature() method to register the customer reducer. We do this by specifying the name of the slice of state and the reducer that manages that slice.

So, let’s load some data in our component from the store. For that we’re going to add the following code to the customer.reducer.ts file:

// customers/customer.reducer.ts
const initialState = [
  {
    customers: [
      {
        name: "John Doe",
        phone: "910928392098",
        address: "123 Sun Street",
        membership: "Platinum",
        id: 1
      }
    ]
  }
];

export function customerReducer(state = initialState, action) {
  switch (action.type) {
    case "LOAD_CUSTOMERS": {
      return {
        ...state,
        loading: true,
        loaded: false
      };
    }

    default: {
      return state;
    }
  }
}

Here we’re declaring the initial state. So when the store is initialized it contains some data that we can load in our component. Then, we declare the customerReducer with one action, LOAD_CUSTOMERS, so the reducer will create a new state with the changes.

Now that we have our reducer, we can go ahead and dispatch an action from our component. Add the following code to the customer.component.ts file:

// customers/customer.component.ts
import { Component, OnInit } from "@angular/core";
import { Store } from "@ngrx/store";

@Component({
  selector: "app-customer-list",
  templateUrl: "./customer-list.component.html",
  styleUrls: ["./customer-list.component.css"]
})
export class CustomerListComponent implements OnInit {
  customers;

  constructor(private store: Store<any>) {}

  ngOnInit() {
    this.store.dispatch({ type: "LOAD_CUSTOMERS" });
    this.store.subscribe(state => (this.customers = state.customers.customers));
  }
}

Here we import the store, initialise the customers and inject the store in the constructor. We dispatch an action when the component is initialised and subscribe to the store. This is to get the slice of the state containing customers to assign them to the customers in our component. So now we can update the template to display the list of customers.

// customers/customer-list.component.html
<h3>Customers</h3>
<table class="table table-hover">
  <thead>
    <tr class="table-primary">
      <th scope="col">Name</th>
      <th scope="col">Phone</th>
      <th scope="col">Address</th>
      <th scope="col">Membership</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <tr *ngFor="let customer of customers">
        <th scope="row">{{customer.name}}</th>
        <td>{{customer.phone}}</td>
        <td>{{customer.address}}</td>
        <td>{{customer.membership}}</td>
        <th>
          <a>edit</a>
          <br>
          <a>delete</a>
        </th>
      </tr>
  </tbody>
</table>

If we refresh the application we should see the customers being loaded into our component from the store. It’s a basic example to illustrate how the component, the action, and the reducer work together. During the rest of this course, we’ll be introducing more convenient ways to use them and some other best practices.

In the next part we’ll set up the store developers tool. This is so we can inspect our application in our browser while we’re developing. We will also continue building the application and introducing other important concepts.