TL;DR Angular, previously known as Angular 2, incorporates RxJS and uses it internally. We can make use of some RxJS goodies and introduce FRP to write more robust code in our apps.


If you're new to RxJS, I recommend reading Understanding Reactive Programming and RxJS before proceeding.

RxJS is all about streams, operators to modify them, and observables.

Functional Reactive Programming (FRP)

FRP has recently become a buzzword. To give you a deeper understanding on that topic, there is an awesome post from Andre Stalz -- The introduction to Reactive Programming you've been missing. What is the key takeaway from the this comprehensive post? Reactive programming is actually programming with asynchronous data streams. But where does the word functional come into play? Functional is about how we can modify these streams to create new sets of data. A stream can be used as an input to another stream. We have a bunch of operators in RxJS to do things like this. So, can we do some of FRP with RxJS? The short answer is: yes! And we'll do so with Angular.

RxJS in Angular

To get started with RxJS in Angular, all we need to do is import the operators we want to use. TRxJS is itself an Angular dependency so it's ready to use out of the box.

Passing observables to the view

We are about to start with some observables created ad hoc. Let's create an observable from the JavaScript array:

const items = Observable.of([1, 2, 3])

Now, we can use the created observable as a component's property and pass it into the view. Angular introduced a new filter, which will be a perfect fit here. It's called async. Its purpose is to unwrap promises and observables. In the case of an observable it'll pass the last value of the observable:

import { Component } from '@angular/core'
import { Observable } from 'rxjs/Rx'

@Component({
  selector: 'my-app',
  template: `
    <ul>
      <li *ngFor="let item of items | async">

      </li>
    </ul>
  `
})
export class AppComponent {
  public items = Observable.of([1, 2, 3])
}

We should see a list of elements in the browser.

This is our hello world example to see how async works and how we can use it.

Http

Angular relies on RxJS for some of its internal features. One of the most well-known services is Http. In Angular 1.x, Http was a promise-based service. In Angular 2+, it's based on observables. This means that we can also make use of the async pipe here. Let's try to create a real-world example with a service. We want to fetch a list of repos authored by Auth0 on GitHub:

import { Injectable } from '@angular/core'
import { Http } from '@angular/http'
import { Observable } from 'rxjs/Rx'
import 'rxjs/add/operator/map'

@Injectable()
export class RepoService {
  constructor(private _http: Http) {}

  getReposForUser(user: string): Observable<any> {
    return this._http
      .get(`https://api.github.com/users/${user}/repos`)
      .map((res: any) => res.json())
  }
}

Here, we have the service, which exposes the getReposForUser method to make an http call. Note the return type of the method -- it's an Observable<any>. Now, we can add it into the module and use it in the component:

import { RepoService } from './repo.service.js'

@Component({
  selector: 'my-app',
  template: `

  `,
})
export class AppComponent {
  public repos: Observable<any>

  constructor(repoService: RepoService) {
    this.repos = repoService.getReposForUser('auth0')
    console.log(this.repos)
  }
}

Something important has just happened. You can take a look into the Network tab of your developer tools in the browser. No call was made. Let's add the for loop with the async pipe:

@Component({
  selector: 'my-app',
  template: `
    <ul>
      <li *ngFor="let repo of repos | async">

      </li>
    </ul>
  `,
})
export class AppComponent {
  public repos: Observable<any>

  constructor(repoService: RepoService) {
    this.repos = repoService.getReposForUser('auth0')
  }
}

Now the call for repositories is fired, and we can see that the list of repos has been fetched correctly. Why is that?

Hot and Cold Observables

The http.get observable above is cold: that means each subscriber sees the same events from the beginning. It's independent of any other subscriber. It also means that if there's no subscriber, no value is emitted! See this one in action:

export class AppComponent {
  public repos: Observable<any>

  constructor(repoService: RepoService) {
    this.repos = repoService.getReposForUser('auth0')
    this.repos.subscribe()
    this.repos.subscribe()
  }
}

Now you'll be able to see three calls. You can now see one more thing -- async makes a subscription under the hood.

On the other hand, we have hot observables. The difference is, no matter how many subscribers there are, the observable starts just once. And we can make our observable hot, instead of cold, by using the share operator:

// ...
import 'rxjs/add/operator/share'

@Injectable()
export class RepoService {
  constructor(private http: Http) {}

  getReposForUser(user: string): Observable<any> {
    return this.http
      .get(`https://api.github.com/users/${user}/repos`)
      .map((res: any) => res.json())
      .share()
  }
}

Now you should see just one call. If you want to go deeper with the topic, here is a Hot vs Cold Observables article by Ben Lesh.

Programming the reactive way in Angular

Handling events

We've covered how you've probably used RxJS observables for Http in Angular, even if you weren't aware of it. However, there are many more things you can do with streams, even if Angular doesn't require you to do so. Now we move on to the on click events. The traditional, imperative way of handling click events in Angular is as follows:

@Component({
  selector: 'my-app',
  template: `
    <button (click)="handleButtonClick(1)">
      Up Vote
    </button>
  `,
})
export class AppComponent {
  handleButtonClick(value: number) {
    console.log(value)
  }
}

We can create a stream of click events using RxJS Subject. Subject is both an observer and an observable at the same time. It means it can emit value (using .next()), and you can subscribe to it (using subscribe).

Here, you can see the same case achieved with functional approach using RxJS:

@Component({
  selector: 'my-app',
  template: `
    <button (click)="counter$.next(1)">
      Up Vote
    </button>
  `,
})
export class AppComponent {
  public counter$: Observable<number> = new Subject<number>()

  constructor() {
    this.counter$.subscribe(console.log.bind(console))
  }
}

It's not much different than the previous one, though. Let's try to add some more logic there. Like making sum of clicks and printing some text instead of just number.

@Component({
  selector: 'my-app',
  template: `
    <button (click)="counter$.next(1)">
      Up Vote
    </button>
  `,
})
export class AppComponent {
  public counter$: Observable<string> = new Subject<number>()
    .scan((acc: number, current: number): number => acc + current)
    .map((value: number): string => `Sum of clicks: ${value}`)

  constructor() {
    this.counter$.subscribe(console.log.bind(console))
  }
}

The key point is that we define how the clicks stream will behave. We say that we don't really need clicks but only the sum of them with some prepended text. And this sum will be our stream, not the pure click events. And we subscribe to the stream of summed values. In other words, the key of functional programming is to make the code declarative, not imperative.

Communication between components

Let's briefly address communication between Angular components using an RxJS approach. It's actually about dumb components in the RxJS approach of an Angular world. Last time I described the change detection of Angular and what we can do with it to fine-tune the app. We'll add the component with clicks$ stream as the input.

import { ChangeDetectionStrategy, Component, Input } from '@angular/core'

@Component({
  selector: 'my-score',
  template: 'Summary: ',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ScoreComponent {
  @Input() public score: number
}

Note that the component has ChangeDetectionStrategy.OnPush turned on, so this means that we assume that the new reference will come as the input. The component accepts a numeric parameter, but there is no reference to streams. We can handle this with the async pipe:

@Component({
  selector: 'my-app',
  template: `
    <button (click)="counter$.next(1)">
      Up Vote
    </button>
    <my-score [score]="counter$ | async"></my-score>
  `,
})
export class AppComponent {
  public counter$: Observable<number> = new Subject<number>()
    .scan((acc: number, current: number): number => acc + current)
}

Forms

Another place when you can use the power of RxJS is forms. We can use all of the knowledge that we have gained up to this point and see how we can create a reactive login form.

First, let's start with adding ReactiveFormsModule from @angular/forms to the module. Then we can make use of the reactive forms introduced in Angular. Here's how it can look:

import { FormBuilder, FormGroup } from '@angular/forms'

@Component({
  selector: 'my-app',
  template: `
    <form
      [formGroup]="loginForm"
    >
      <label>Login:</label>
      <input
        formControlName="login"
        type="text"
      >

      <label>Password:</label>
      <input
        formControlName="password"
        type="password"
      >

      <button type="submit">Submit</button>
    </form>
  `,
})
export class AppComponent implements OnInit {
  public loginForm: FormGroup

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.loginForm = this.formBuilder.group({
      login: '',
      password: '',
    })
  }
}

We now have a few additional blocks: - formControlName -- added to match names from templates to the appropriate fields in the controller - formBuilder.group -- creates the form - [formGroup] -- connects the template and the controller

We can now use the valueChanges observable:

// ...
    this.loginForm.valueChanges.subscribe(console.log.bind(console))
// ...

Now, each changed field will emit an event and will be logged to the console. This offers many possibilities since we can take advantage of any operator that RxJS provides. In this example, let's focus on submitting the form in a reactive way. We can put (submit) on the form:

@Component({
  selector: 'my-app',
  template: `
    <form
      [formGroup]="loginForm"
      (submit)="submit$.next()"
    >
    <!-- ... -->
  `
})
// ...
export class AppComponent {
  public loginForm: FormGroup
  private submit$: Observable<any> = new Subject()

// ...

We now have a stream of submit events and a stream of values. All that remains is to combine these streams. The resulting stream will emit the current state of the fields when the form is submitted. The desired behavior can be achieved by using the withLatestFrom operator of RxJS. The combined stream is as follows:

// ...
    this.submit$
      .withLatestFrom(this.loginForm.valueChanges, (_, values) => values)
      .subscribe(values => {
        console.log('submitted values', values)
      })
// ...

We now have combined streams, and the logic is consolidated. It can be written in a single line. Just to recap, here is the final code for the form component:

@Component({
  selector: 'my-app',
  template: `
    <form
      [formGroup]="loginForm"
      (submit)="submit$.next()"
    >
      <label>Login:</label>
      <input
        formControlName="login"
        type="text"
      >

      <label>Password:</label>
      <input
        formControlName="password"
        type="password"
      >

      <button type="submit">Submit</button>
    </form>
  `,
})
export class AppComponent {
  public loginForm: FormGroup
  private submit$: Observable<any> = new Subject()

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.loginForm = this.formBuilder.group({
      login: '',
      password: '',
    })

    this.submit$
      .withLatestFrom(this.loginForm.valueChanges, (_, values) => values)
      .subscribe(values => {
        console.log('submitted values', values)
      })
  }
}

Aside: Authenticate an Angular App and Node API with Auth0

We can protect our applications and APIs so that only authenticated users can access them. Let's explore how to do this with an Angular application and a Node API using Auth0. You can clone this sample app and API from the angular-auth0-aside repo on GitHub.

Auth0 login screen

Features

The sample Angular application and API has the following features:

  • Angular application generated with Angular CLI and served at http://localhost:4200
  • Authentication with auth0.js using a login page
  • Node server protected API route http://localhost:3001/api/dragons returns JSON data for authenticated GET requests
  • Angular app fetches data from API once user is authenticated with Auth0
  • Profile page requires authentication for access using route guards
  • Authentication service uses a subject to propagate authentication status events to the entire app
  • User profile is fetched on authentication and stored in authentication service
  • Access token, profile, and token expiration are stored in local storage and removed upon logout

Sign Up for Auth0

You'll need an Auth0 account to manage authentication. You can sign up for a free account here. Next, set up an Auth0 application and API so Auth0 can interface with an Angular app and Node API.

Set Up an Auth0 Application

  1. Go to your Auth0 Dashboard and click the "create a new application" button.
  2. Name your new app and select "Single Page Web Applications".
  3. In the Settings for your new Auth0 app, add http://localhost:4200/callback to the Allowed Callback URLs. Click the "Save Changes" button.
  4. If you'd like, you can set up some social connections. You can then enable them for your app in the Application options under the Connections tab. The example shown in the screenshot above utilizes username/password database, Facebook, Google, and Twitter. For production, make sure you set up your own social keys and do not leave social connections set to use Auth0 dev keys.

Note: Under the OAuth tab of Advanced Settings (at the bottom of the Settings section) you should see that the JsonWebToken Signature Algorithm is set to RS256. This is the default for new applications. If it is set to HS256, please change it to RS256. You can read more about RS256 vs. HS256 JWT signing algorithms here.

Set Up an API

  1. Go to APIs in your Auth0 dashboard and click on the "Create API" button. Enter a name for the API. Set the Identifier to your API endpoint URL. In this example, this is http://localhost:3001/api/. The Signing Algorithm should be RS256.
  2. You can consult the Node.js example under the Quick Start tab in your new API's settings. We'll implement our Node API in this fashion, using Express, express-jwt, and jwks-rsa.

We're now ready to implement Auth0 authentication on both our Angular client and Node backend API.

Dependencies and Setup

The Angular app utilizes the Angular CLI. Make sure you have the CLI installed globally:

$ npm install -g @angular/cli

Once you've cloned the project, install the Node dependencies for both the Angular app and the Node server by running the following commands in the root of your project folder:

$ npm install
$ cd server
$ npm install

The Node API is located in the /server folder at the root of our sample application.

Find the config.js.example file and remove the .example extension from the filename. Then open the file:

// server/config.js (formerly config.js.example)
module.exports = {
  CLIENT_DOMAIN: '[AUTH0_CLIENT_DOMAIN]', // e.g. 'you.auth0.com'
  AUTH0_AUDIENCE: 'http://localhost:3001/api/'
};

Change the AUTH0_CLIENT_DOMAIN identifier to your Auth0 application domain and set the AUTH0_AUDIENCE to your audience (in this example, this is http://localhost:3001/api/). The /api/dragons route will be protected with express-jwt and jwks-rsa.

Note: To learn more about RS256 and JSON Web Key Set, read Navigating RS256 and JWKS.

Our API is now protected, so let's make sure that our Angular application can also interface with Auth0. To do this, we'll activate the src/app/auth/auth0-variables.ts.example file by deleting .example from the file extension. Then open the file and change the [AUTH0_CLIENT_ID] and [AUTH0_CLIENT_DOMAIN] strings to your Auth0 information:

// src/app/auth/auth0-variables.ts (formerly auth0-variables.ts.example)
...
export const AUTH_CONFIG: AuthConfig = {
  CLIENT_ID: '[AUTH0_CLIENT_ID]',
  CLIENT_DOMAIN: '[AUTH0_CLIENT_DOMAIN]',
  ...

Our app and API are now set up. They can be served by running ng serve from the root folder and node server.js from the /server folder.

With the Node API and Angular app running, let's take a look at how authentication is implemented.

Authentication Service

Authentication logic on the front end is handled with an AuthService authentication service: src/app/auth/auth.service.ts file.

// src/app/auth/auth.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import * as auth0 from 'auth0-js';
import { AUTH_CONFIG } from './auth0-variables';
import { UserProfile } from './profile.model';

@Injectable()
export class AuthService {
  // Create Auth0 web auth instance
  // @TODO: Update AUTH_CONFIG and remove .example extension in src/app/auth/auth0-variables.ts.example
  private _auth0 = new auth0.WebAuth({
    clientID: AUTH_CONFIG.CLIENT_ID,
    domain: AUTH_CONFIG.CLIENT_DOMAIN,
    responseType: 'token',
    redirectUri: AUTH_CONFIG.REDIRECT,
    audience: AUTH_CONFIG.AUDIENCE,
    scope: AUTH_CONFIG.SCOPE
  });
  userProfile: UserProfile;
  accessToken: string;

  // Create a stream of logged in status to communicate throughout app
  loggedIn: boolean;
  loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);

  constructor() {
    // You can restore an unexpired authentication session on init
    // by using the checkSession() endpoint from auth0.js:
    // https://auth0.com/docs/libraries/auth0js/v9#using-checksession-to-acquire-new-tokens
  }

  private _setLoggedIn(value: boolean) {
    // Update login status subject
    this.loggedIn$.next(value);
    this.loggedIn = value;
  }

  login() {
    // Auth0 authorize request
    this._auth0.authorize();
  }

  handleLoginCallback() {
    // When Auth0 hash parsed, get profile
    this._auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken) {
        window.location.hash = '';
        this.getUserInfo(authResult);
      } else if (err) {
        console.error(`Error: ${err.error}`);
      }
    });
  }

  getUserInfo(authResult) {
    // Use access token to retrieve user's profile and set session
    this._auth0.client.userInfo(authResult.accessToken, (err, profile) => {
      this._setSession(authResult, profile);
    });
  }

  private _setSession(authResult, profile) {
    const expTime = authResult.expiresIn * 1000 + Date.now();
    // Save session data and update login status subject
    localStorage.setItem('expires_at', JSON.stringify(expTime));
    this.accessToken = authResult.accessToken;
    this.userProfile = profile;
    this._setLoggedIn(true);
  }

  logout() {
    // Remove token and profile and update login status subject
    localStorage.removeItem('expires_at');
    this.accessToken = undefined;
    this.userProfile = undefined;
    this._setLoggedIn(false);
  }

  get authenticated(): boolean {
    // Check if current date is greater than expiration
    // and user is currently logged in
    const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    return (Date.now() < expiresAt) && this.loggedIn;
  }

}

This service uses the config variables from auth0-variables.ts to instantiate an auth0.js WebAuth instance.

An RxJS BehaviorSubject is used to provide a stream of authentication status events that you can subscribe to anywhere in the app.

The login() method authorizes the authentication request with Auth0 using your config variables. A login page will be shown to the user and they can then log in.

Note: If it's the user's first visit to our app and our callback is on localhost, they'll also be presented with a consent screen where they can grant access to our API. A first party client on a non-localhost domain would be highly trusted, so the consent dialog would not be presented in this case. You can modify this by editing your Auth0 Dashboard API Settings. Look for the "Allow Skipping User Consent" toggle.

We'll receive accessToken and expiresIn in the hash from Auth0 when returning to our app. The handleLoginCallback() method uses Auth0's parseHash() method callback to get the user's profile (getUserInfo()) and set the session (_setSession()) by saving the token, profile, and token expiration and updating the loggedIn$ subject so that any subscribed components in the app are informed that the user is now authenticated.

Note: The profile takes the shape of profile.model.ts from the OpenID standard claims.

Finally, we have a logout() method that clears data from and updates the loggedIn$ subject. We also have an authenticated accessor to return current authentication status based on presence of a token and the token's expiration.

Once AuthService is provided in app.module.ts, its methods and properties can be used anywhere in our app, such as the home component.

Callback Component

The callback component is where the app is redirected after authentication. This component simply shows a loading message until the login process is completed. It executes the handleLoginCallback() method to parse the hash and extract authentication information. It subscribes to the loggedIn$ Behavior Subject from our Authentication service in order to redirect back to the home page once the user is logged in, like so:

// src/app/callback/callback.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { AuthService } from './../auth/auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-callback',
  templateUrl: './callback.component.html',
  styleUrls: ['./callback.component.css']
})
export class CallbackComponent implements OnInit, OnDestroy {
  loggedInSub: Subscription;

  constructor(private auth: AuthService, private router: Router) {
    // Parse authentication hash
    auth.handleLoginCallback();
  }

  ngOnInit() {
    this.loggedInSub = this.auth.loggedIn$.subscribe(
      loggedIn => loggedIn ? this.router.navigate(['/']) : null
    )
  }

  ngOnDestroy() {
    this.loggedInSub.unsubscribe();
  }

}

Making Authenticated API Requests

In order to make authenticated HTTP requests, we need to add an Authorization header with the access token in our api.service.ts file.

// src/app/api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { catchError } from 'rxjs/operators';
import 'rxjs/add/observable/throw';
import { AuthService } from './auth/auth.service';

@Injectable()
export class ApiService {
  private baseUrl = 'http://localhost:3001/api/';

  constructor(
    private http: HttpClient,
    private auth: AuthService
  ) { }

  getDragons$(): Observable<any[]> {
    return this.http
      .get(`${this.baseUrl}dragons`, {
        headers: new HttpHeaders().set(
          'Authorization', `Bearer ${this.auth.accessToken}`
        )
      })
      .pipe(
        catchError(this._handleError)
      );
  }

  private _handleError(err: HttpErrorResponse | any) {
    const errorMsg = err.message || 'Unable to retrieve data';
    return Observable.throw(errorMsg);
  }

}

Final Touches: Route Guard and Profile Page

A profile page component can show an authenticated user's profile information. However, we only want this component to be accessible if the user is logged in.

With an authenticated API request and login/logout implemented, the final touch is to protect our profile route from unauthorized access. The auth.guard.ts route guard can check authentication and activate routes conditionally. The guard is implemented on specific routes of our choosing in the app-routing.module.ts file like so:

// src/app/app-routing.module.ts
...
import { AuthGuard } from './auth/auth.guard';
...
      {
        path: 'profile',
        component: ProfileComponent,
        canActivate: [
          AuthGuard
        ]
      },
...

More Resources

That's it! We have an authenticated Node API and Angular application with login, logout, profile information, and protected routes. To learn more, check out the following resources:

Conclusion

Angular has a lot more features than meets the eye. RxJS is, in my personal opinion, one of the best of them. It can rocket the app to the next level in term of maintainability and clarity. The future is more declarative, less imperative code.

RxJS can appear intimidating at first, but once you’re familiar with its functionality and operators, it supplies many benefits (such as defining logic at the declaration time). All of this is to say, the code is easier to understand compared to imperative code. RxJS requires a different mode of thinking, but it is very worthwhile to learn and use.