Technology13 minute read

Ngrx and Angular 2 Tutorial: Building a Reactive Application

Building a reactive web application is a lot more about how you handle events and data flow in your applications than the tools you use to do so. However, Angular 2 with Ngrx seems to be the perfect combination for building reactive applications for many reasons.

In this article, Toptal Freelance Software Engineer Simon Boissonneault-Robert walks you through a reactive web application tutorial using Angular and Ngrx and shows how these two technologies make it easy to do that.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

Building a reactive web application is a lot more about how you handle events and data flow in your applications than the tools you use to do so. However, Angular 2 with Ngrx seems to be the perfect combination for building reactive applications for many reasons.

In this article, Toptal Freelance Software Engineer Simon Boissonneault-Robert walks you through a reactive web application tutorial using Angular and Ngrx and shows how these two technologies make it easy to do that.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Simon Boissonneault-Robert
Verified Expert in Engineering

Simon, M.C.E., has full-stack experience covering private- and public-sector projects of all sizes. He focuses on high-quality front ends.

Read More

Expertise

PREVIOUSLY AT

Wolters Kluwer
Share

We talk a lot about reactive programming in the Angular realm. Reactive programming and Angular 2 seem to go hand in hand. However, for anyone not familiar with both technologies, it can be quite a daunting task to figure out what it is all about.

In this article, through building a reactive Angular 2 application using Ngrx, you will learn what the pattern is, where the pattern can prove to be useful, and how the pattern can be used to build better Angular 2 applications.

Ngrx is a group of Angular libraries for reactive extensions. Ngrx/Store implements the Redux pattern using the well-known RxJS observables of Angular 2. It provides several advantages by simplifying your application state to plain objects, enforcing unidirectional data flow, and more. The Ngrx/Effects library allows the application to communicate with the outside world by triggering side effects.

What Is Reactive Programming?

Reactive programming is a term that you hear a lot these days, but what does it really mean?

Reactive programming is a way applications handle events and data flow in your applications. In reactive programming, you design your components and other pieces of your software in order to react to those changes instead of asking for changes. This can be a great shift.

A great tool for reactive programming, as you might know, is RxJS.

By providing observables and a lot of operators to transform incoming data, this library will help you handle events in your application. In fact, with observables, you can see event as a stream of events and not a one-time event. This allows you to combine them, for example, to create a new event to which you will listen.

Reactive programming is a shift in the way you communicate between different parts of an application. Instead of pushing data directly to the component or service that needed it, in reactive programming, it is the component or service that reacts to data changes.

A Word about Ngrx

In order to understand the application you will build through this tutorial, you must make a quick dive into the core Redux concepts.

Store

The store can be seen as your client side database but, more importantly, it reflects the state of your application. You can see it as the single source of truth.

It is the only thing you alter when you follow the Redux pattern and you modify by dispatching actions to it.

Reducer

Reducers are the functions that know what to do with a given action and the previous state of your app.

The reducers will take the previous state from your store and apply a pure function to it. Pure means that the function always returns the same value for the same input and that it has no side effects. From the result of that pure function, you will have a new state that will be put in your store.

Actions

Actions are the payload that contains needed information to alter your store. Basically, an action has a type and a payload that your reducer function will take to alter the state.

Dispatcher

Dispatchers are simply an entry point for you to dispatch your action. In Ngrx, there is a dispatch method directly on the store.

Middleware

Middleware are some functions that will intercept each action that is being dispatched in order to create side effects, even though you will not use them in this article. They are implemented in the Ngrx/Effect library, and there is a big chance that you will need them while building real-world applications.

Why Use Ngrx?

Complexity

The store and unidirectional data flow greatly reduce coupling between parts of your application. This reduced coupling reduces the complexity of your application, since each part only cares about specific states.

Tooling

The entire state of your application is stored in one place, so it is easy to have a global view of your application state and helps during development. Also, with Redux comes a lot of nice dev tools that take advantage of the store and can help to reproduce a certain state of the application or make time travel, for example.

Architectural simplicity

Many of the benefits of Ngrx are achievable with other solutions; after all, Redux is an architectural pattern. But when you have to build an application that is a great fit for the Redux pattern, such as collaborative editing tools, you can easily add features by following the pattern.

Although you don’t have to think about what you are doing, adding some things like analytics through all your applications becomes trivial since you can track all the actions that are dispatched.

Small learning curve

Since this pattern is so widely adopted and simple, it is really easy for new people in your team to catch up quickly on what you did.

Ngrx shines the most when you have a lot of external actors that can modify your application, such as a monitoring dashboard. In those cases, it is hard to manage all the incoming data that are pushed to your application, and state management becomes hard. That is why you want to simplify it with an immutable state, and this is one thing that the Ngrx store provides us with.

Building an Application with Ngrx

The power of Ngrx shines the most when you have outside data that is being pushed to our application in real time. With that in mind, let’s build a simple freelancer grid that shows online freelancers and allows you to filter through them.

Setting Up the Project

Angular CLI is an awesome tool that greatly simplifies the setup process. You may want to not use it but keep in mind that the rest of this article will use it.

npm install -g @angular/cli

Next, you want to create a new application and install all Ngrx libraries:

ng new toptal-freelancers
npm install ngrx --save

Freelancers Reducer

Reducers are a core piece of the Redux architecture, so why not start with them first while building the application?

First, create a “freelancers” reducer that will be responsible for creating our new state each time an action is dispatched to the store.

freelancer-grid/freelancers.reducer.ts

import { Action } from '@ngrx/store';

export interface AppState {
    freelancers : Array<IFreelancer>
}

export interface IFreelancer {
    name: string,
    email: string,
    thumbnail: string
}

export const ACTIONS = {
    FREELANCERS_LOADED: 'FREELANCERS_LOADED',
}

export function freelancersReducer(
    state: Array<IFreelancer> = [],
    action: Action): Array<IFreelancer> {
    switch (action.type) {
        case ACTIONS.FREELANCERS_LOADED:
            // Return the new state with the payload as freelancers list
            return Array.prototype.concat(action.payload);
        default:
            return state;
    }
}

So here is our freelancers reducer.

This function will be called each time an action is dispatched through the store. If the action is FREELANCERS_LOADED, it will create a new array from the action payload. If it is not, it will return the old state reference and nothing will be appended.

It is important to note here that, if the old state reference is returned, the state will be considered unchanged. This means that if you call a state.push(something), the state will not be considered to have changed. Keep that in mind while doing your reducer functions.

States are immutable. A new state must be returned each time it changes.

Freelancer Grid Component

Create a grid component to show our online freelancers. At first, it will only reflect what is in the store.

ng generate component freelancer-grid

Put the following in freelancer-grid.component.ts

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState, IFreelancer, ACTIONS } from './freelancer-reducer';
import * as Rx from 'RxJS';

@Component({
  selector: 'app-freelancer-grid',
  templateUrl: './freelancer-grid.component.html',
  styleUrls: ['./freelancer-grid.component.scss'],
})
export class FreelancerGridComponent implements OnInit {
  public freelancers: Rx.Observable<Array<IFreelancer>>;

  constructor(private store: Store<AppState>) {
    this.freelancers = store.select('freelancers');
  }

}

And the following in freelancer-grid.component.html:

<span class="count">Number of freelancers online: {{(freelancers | async).length}}</span>
<div class="freelancer fade thumbail" *ngFor="let freelancer of freelancers | async">
    <button type="button" class="close" aria-label="Close" (click)="delete(freelancer)"><span aria-hidden="true">&times;</span></button><br>
    <img class="img-circle center-block" src="{{freelancer.thumbnail}}" /><br>
    <div class="info"><span><strong>Name: </strong>{{freelancer.name}}</span>
        <span><strong>Email: </strong>{{freelancer.email}}</span></div>
    <a class="btn btn-default">Hire {{freelancer.name}}</a>
</div>

So what did you just do?

First, you have created a new component called freelancer-grid.

The component contains a property named freelancers that is a part of the application state contained in the Ngrx store. By using the select operator, you choose to only be notified by the freelancers property of the overall application state. So now each time the freelancers property of the application state changes, your observable will be notified.

One thing that is beautiful with this solution is that your component has only one dependency, and it is the store that makes your component much less complex and easily reusable.

On the template part, you did nothing too complex. Notice the use of async pipe in the *ngFor. The freelancers observable is not directly iterable, but thanks to Angular, we have the tools to unwrap it and bind the dom to its value by using the async pipe. This makes working with the observable so much easier.

Adding the Remove Freelancers Functionality

Now that you have a functional base, let’s add some actions to the application.

You want to be able to remove a freelancer from the state. According to how Redux works, you need to first define that action in each state that are affected by it.

In this case, it is only the freelancers reducer:

export const ACTIONS = {
    FREELANCERS_LOADED: 'FREELANCERS_LOADED',
    DELETE_FREELANCER: 'DELETE_FREELANCER',
}

export function freelancersReducer(
    state: Array<IFreelancer> = [],
    action: Action): Array<IFreelancer> {
    switch (action.type) {
        case ACTIONS.FREELANCERS_LOADED:
            // Return the new state with the payload as freelancers list
            return Array.prototype.concat(action.payload);
        case ACTIONS.DELETE_FREELANCER:
            // Remove the element from the array
            state.splice(state.indexOf(action.payload), 1);
            // We need to create another reference
            return Array.prototype.concat(state);
       default:
            return state;
    }
}

It is really important here to create a new array from the old one in order to have a new immutable state.

Now, you can add a delete freelancers function to your component that will dispatch this action to the store:

delete(freelancer) {
    this.store.dispatch({
      type: ACTIONS.DELETE_FREELANCER,
      payload: freelancer,
    })
  }

Doesn’t that look simple?

You can now remove a specific freelancer from the state, and that change will propagate through your application.

Now what if you add another component to the application to see how they can interact between each other through the store?

Filter Reducer

As always, let’s start with the reducer. For that component, it is quite simple. You want the reducer to always return a new state with only the property that we dispatched. It should looks like this:

import { Action } from '@ngrx/store';

export interface IFilter {
    name: string,
    email: string,
}

export const ACTIONS = {
    UPDATE_FITLER: 'UPDATE_FITLER',
    CLEAR_FITLER: 'CLEAR_FITLER',
}

const initialState = { name: '', email: '' };

export function filterReducer(
    state: IFilter = initialState,
    action: Action): IFilter {
    switch (action.type) {
        case ACTIONS.UPDATE_FITLER:
            // Create a new state from payload
            return Object.assign({}, action.payload);
        case ACTIONS.CLEAR_FITLER:
            // Create a new state from initial state
            return Object.assign({}, initialState);
        default:
            return state;
    }
}

Filter Component

import { Component, OnInit } from '@angular/core';
import { IFilter, ACTIONS as FilterACTIONS } from './filter-reducer';
import { Store } from '@ngrx/store';
import { FormGroup, FormControl } from '@angular/forms';
import * as Rx from 'RxJS';

@Component({
  selector: 'app-filter',
  template: 
    '<form class="filter">'+
    '<label>Name</label>'+
    '<input type="text" [formControl]="name" name="name"/>'+
    '<label>Email</label>'+
    '<input type="text" [formControl]="email" name="email"/>'+
    '<a (click)="clearFilter()" class="btn btn-default">Clear Filter</a>'+
    '</form>',
  styleUrls: ['./filter.component.scss'],
})
export class FilterComponent implements OnInit {

  public name = new FormControl();
  public email = new FormControl();
  constructor(private store: Store<any>) {
    store.select('filter').subscribe((filter: IFilter) => {
      this.name.setValue(filter.name);
      this.email.setValue(filter.email);
    })
    Rx.Observable.merge(this.name.valueChanges, this.email.valueChanges).debounceTime(1000).subscribe(() => this.filter());
  }

  ngOnInit() {
  }

  filter() {
    this.store.dispatch({
      type: FilterACTIONS.UPDATE_FITLER,
      payload: {
        name: this.name.value,
        email: this.email.value,
      }
    });
  }

  clearFilter() {
    this.store.dispatch({
      type: FilterACTIONS.CLEAR_FITLER,
    })
  }

}

First, you have made a simple template that includes a form with two fields (name and email) that reflects our state.

You keep those fields in sync with state quite a bit differently than what you did with the freelancers state. In fact, as you have seen, you subscribed to the filter state, and each time, it triggers you assign the new value to the formControl.

One thing that is nice with Angular 2 is that it provides you with a lot of tools to interact with observables.

You have seen the async pipe earlier, and now you see the formControl class that allows you to have an observable on the value of an input. This allows fancy things like what you did in the filter component.

As you can see, you use Rx.observable.merge to combine the two observables given by your formControls, and then you debounce that new observable before triggering the filter function.

In simpler words, you wait one second after either of the name or email formControl have changed and then call the filter function.

Isn’t that awesome?

All of that is done in a few lines of code. This is one of the reasons why you will love RxJS. It allows you to do a lot of those fancy things easily that would have been more complicated otherwise.

Now let’s step to that filter function. What does it do?

It simply dispatches the UPDATE_FILTER action with the value of the name and the email, and the reducer takes care of altering the state with that information.

Let’s move on to something more interesting.

How do you make that filter interact with your previously created freelancer grid?

Simple. You only have to listen to the filter part of the store. Let’s see what the code looks like.

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState, IFreelancer, ACTIONS } from './freelancer-reducer';
import { IFilter, ACTIONS as FilterACTIONS } from './../filter/filter-reducer';
import * as Rx from 'RxJS';

@Component({
  selector: 'app-freelancer-grid',
  templateUrl: './freelancer-grid.component',
  styleUrls: ['./freelancer-grid.component.scss'],
})
export class FreelancerGridComponent implements OnInit {
  public freelancers: Rx.Observable<Array<IFreelancer>>;
  public filter: Rx.Observable<IFilter>;

  constructor(private store: Store<AppState>) {
    this.freelancers = Rx.Observable.combineLatest(store.select('freelancers'), store.select('filter'), this.applyFilter);
  }

  applyFilter(freelancers: Array<IFreelancer>, filter: IFilter): Array<IFreelancer> {
    return freelancers
      .filter(x => !filter.name || x.name.toLowerCase().indexOf(filter.name.toLowerCase()) !== -1)
      .filter(x => !filter.email || x.email.toLowerCase().indexOf(filter.email.toLowerCase()) !== -1)
  }

  ngOnInit() {
  }

  delete(freelancer) {
    this.store.dispatch({
      type: ACTIONS.DELETE_FREELANCER,
      payload: freelancer,
    })
  }

}

It is no more complicated than that.

Once again, you used the power of RxJS to combine the filter and freelancers state.

In fact, combineLatest will fire if one of the two observables fire and then combine each state using the applyFilter function. It returns a new observable that do so. We don’t have to change any other lines of code.

Notice how the component does not care about how the filter is obtained, modified, or stored; it only listens to it as it would do for any other state. We just added the filter functionality and we did not add any new dependencies.

Making It Shine

Remember that the use of Ngrx really shines when we have to deal with real time data? Let’s add that part to our application and see how it goes.

Introducing the freelancers-service.

ng generate service freelancer

The freelancer service will simulate real time operation on data and should looks like this.

import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState, IFreelancer, ACTIONS } from './freelancer-grid/freelancer-reducer';
import { Http, Response } from '@angular/http';

@Injectable()
export class RealtimeFreelancersService {

  private USER_API_URL = 'https://randomuser.me/api/?results='

  constructor(private store: Store<AppState>, private http: Http) { }

  private toFreelancer(value: any) {
    return {
      name: value.name.first + ' ' + value.name.last,
      email: value.email,
      thumbail: value.picture.large,
    }
  }

  private random(y) {
    return Math.floor(Math.random() * y);
  }

  public run() {
    this.http.get(`${this.USER_API_URL}51`).subscribe((response) => {
      this.store.dispatch({
        type: ACTIONS.FREELANCERS_LOADED,
        payload: response.json().results.map(this.toFreelancer)
      })
    })

    setInterval(() => {
      this.store.select('freelancers').first().subscribe((freelancers: Array<IFreelancer>) => {
        let getDeletedIndex = () => {
          return this.random(freelancers.length - 1)
        }
        this.http.get(`${this.USER_API_URL}${this.random(10)}`).subscribe((response) => {
          this.store.dispatch({
            type: ACTIONS.INCOMMING_DATA,
            payload: {
              ADD: response.json().results.map(this.toFreelancer),
              DELETE: new Array(this.random(6)).fill(0).map(() => getDeletedIndex()),
            }
          });
          this.addFadeClassToNewElements();
        });
      });
    }, 10000);
  }

  private addFadeClassToNewElements() {
    let elements = window.document.getElementsByClassName('freelancer');
    for (let i = 0; i < elements.length; i++) {
      if (elements.item(i).className.indexOf('fade') === -1) {
        elements.item(i).classList.add('fade');
      }
    }
  }
}

This service is not perfect, but it does what it does and, for demo purposes, it allows us to demonstrate a few things.

First, this service is quite simple. It queries a user API and pushes the results to the store. It is a no-brainer, and you don’t have to think about where the data goes. It goes to the store, which is something that makes Redux so useful and dangerous at the same time—but we will come back to this later. After every ten seconds, the service picks a few freelancers and sends an operation to delete them along with an operation to a few other freelancers.

If we want our reducer to be able to handle it, we need to modify it:

import { Action } from '@ngrx/store';

export interface AppState {
    freelancers : Array<IFreelancer>
}

export interface IFreelancer {
    name: string,
    email: string,
}

export const ACTIONS = {
    LOAD_FREELANCERS: 'LOAD_FREELANCERS',
    INCOMMING_DATA: 'INCOMMING_DATA',
    DELETE_FREELANCER: 'DELETE_FREELANCER',
}

export function freelancersReducer(
    state: Array<IFreelancer> = [],
    action: Action): Array<IFreelancer> {
    switch (action.type) {
        case ACTIONS.INCOMMING_DATA:
            action.payload.DELETE.forEach((index) => {
                state.splice(state.indexOf(action.payload), 1);
            })
            return Array.prototype.concat(action.payload.ADD, state);
        case ACTIONS.FREELANCERS_LOADED:
            // Return the new state with the payload as freelancers list
            return Array.prototype.concat(action.payload);
        case ACTIONS.DELETE_FREELANCER:
            // Remove the element from the array
            state.splice(state.indexOf(action.payload), 1);
            // We need to create another reference
            return Array.prototype.concat(state);
        default:
            return state;
    }
}

Now we are able to handle such operations.

One thing that is demonstrated in that service is that, of all the process of state changes being done synchronously, it is quite important to notice that. If the application of the state was async, the call on this.addFadeClassToNewElements(); would not work as the DOM element would not be created when this function is called.

Personally, I find that quite useful, since it improves predictability.

Building Applications, the Reactive Way

Through this tutorial, you have built a reactive application using Ngrx, RxJS, and Angular 2.

As you have seen, these are powerful tools. What you have built here can also be seen as the implementation of a Redux architecture, and Redux is powerful in itself. However, it also has some constraints. While we use Ngrx, those constraints inevitably reflect in the part of our application that we use.

Reactive Paradigm

The diagram above is a rough of the architecture you just did.

You may notice that even if some components are influencing each other, they are independent of each other. This is a peculiarity of this architecture: Components share a common dependency, which is the store.

Another particular thing about this architecture is that we don’t call functions but dispatch actions. An alternative to Ngrx could be to only make a service that manages a particular state with observables of your applications and call functions on that service instead of actions. This way, you could get centralization and reactiveness of the state while isolating the problematic state. This approach can help you to reduce the overhead of creating a reducer and describe actions as plain objects.

When you feel like the state of your application is being updated from different sources and it starts to become a mess, Ngrx is what you need.

Understanding the basics

  • What is reactive programming?

    Reactive programming is a shift in the way different parts of an application communicate with each other. Instead of pushing data directly to the component or service that needed it, in reactive programming, it is the component or service that reacts to data changes.

  • What is Ngrx?

    Ngrx is a set of Angular libraries for reactive extensions. Two popular Ngrx libraries are Ngrx/Store, an implementation of the Redux pattern using the well-known RxJS observables of Angular 2, and Ngrx/Effects, a library that allows the application to communicate with the outside world by triggering side effects.

Hire a Toptal expert on this topic.
Hire Now
Simon Boissonneault-Robert

Simon Boissonneault-Robert

Verified Expert in Engineering

Sherbrooke, QC, Canada

Member since March 9, 2017

About the author

Simon, M.C.E., has full-stack experience covering private- and public-sector projects of all sizes. He focuses on high-quality front ends.

Read More
authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

Expertise

PREVIOUSLY AT

Wolters Kluwer

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

World-class articles, delivered weekly.

Subscription implies consent to our privacy policy

Join the Toptal® community.