Intro to RxJS & Observables in Angular 2

Sam Julien, Energy Trust of Oregon

What We'll Cover

  • The roots of Functional Reactive Programming
  • RxJS & Observables
  • Simple Counter Example
  • RxJS in Angular 2
  • Typeahead Example
  • Where to go from here

The Hot New Old Thing

  • Core concepts around for 20+ years
  • Why is this such a big thing (again)?
    • More data
    • More complexity
    • Greater demand to be asynchronous
  • Convergence of two big ideas:
    • Unidirectional (one-way) data flow
    • Return of Functional Programming
  • Angular 2!

Functional Programming Oversimplified

  • Around for decades, used a lot in academia
  • Pure functions with no side effects
  • Haskell, Scala, and recently Elm

More on FP

  • FP stresses (Neal Ford):
    • Immutability over state transactions
    • Results over steps
    • Composition over structure
    • Declarative over imperative
  • In JavaScript, array functions like map, filter, and reduce are used in functional approaches
  • Not the be all, end all, just another skill like OOP

Reactive Programming

  • Reactive applications are asynchronous and event-driven
  • Think of an Excel spreadsheet
  • But what kinds of events?

Streams!

  • A stream is a sequence of events over time
  • Anything can be a stream, from mouse clicks to data from a server
  • Streams can then be thought of as arrays of values and operated on as such. For example, mouse click coordinates: [(100,200), (300,400), (110,350)]

Put it Together: Functional Reactive Programming (FRP)

  • Another new old thing: a 20-year-old paradigm
  • Entire applications can be built around streams
  • Create or identify the streams in your application, then combine and subscribe to them

FRP...

  • Allows us to build in a declarative style by defining streams, how they are connected, and what happens as new values arrive over time
  • With little to no application state (state is typically stored on certain streams or the DOM)

RxJS

  • RxJS is the JavaScript version of Microsoft's Reactive Extensions, which enable the creation of reactive applications with something called observables

Using RxJS, developers represent asynchronous data streams with Observablesquery asynchronous data streams using LINQ operators, and parameterize the concurrency in the asynchronous data streams using Schedulers

 

Simply put, Rx = Observables + LINQ + Schedulers.

(Rx Docs)

What are Observables?

  • A data type introduced by RxJS to help us create, subscribe to, and react to streams
  • Asynchronous, push-based sequences of data that can be subscribed to
  • Think of them like an API for the stream, not the stream itself

Structure of an Observable

  • The Observable object contains onNext, onError, and onComplete methods that can invoke methods you specify
  • The observer subscribes to the event stream (the observable). The observable notifies the observer whenever an event occurs.
  • The subscribe method returns a Disposable object that allows you to clean up the subscription when you're done

Common RxJS Operators

var source$ = Rx.Observable.range(1,4); //1,2,3,4

//map (select) & flatMap (selectMany): changes each value 
//flatMap returns an observable so it works well with async operations
source$.map(x => x*2); //2, 4, 6, 8

//filter: returns only selected values based on custom logic
source$.filter(x => x % 2 === 0); //2, 4

//reduce: performs a computation on the stream and outputs the final value
source$.reduce((prev, curr) => prev + curr); //10

//scan: performs a computation on the stream but outputs intermittment values
source$.scan((prev, curr) => prev + curr); //1, 3, 6, 10

Wayne Maurer

RxJS in Angular 2

  • ng2 does not prescribe a data flow architecture, making it flexible and able to be used with MVC or Flux
  • Possibility of a "Model-View-Intent" Architecture (Andre Staltz)
  • Angular 2 uses observables in two ways:
    • Internally, to implement core logic like the EventEmitter
    • In the API, specifically in forms and HTTP, to help data flow between components

RxJS in ng2: Forms

  • Forms can be handled as observables that we subscribe to. The whole form is an observable, and the individual fields are also observables.
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
   <p>
        <label>Payee:</label>
        <input type="text" ngControl="payee">
   </p>
</form>

RxJS in ng2: Forms

  • ngFormModel lets us bind the form to a variable of type ControlGroup in the component
  • We can then access the form observable with form.valueChanges
this.form.valueChanges
        .map((value) => {
            value.payee = value.payee.toLowerCase();
            return value;
        })
        .filter((value) => this.form.valid)
        .subscribe(validValue => ...);

RxJS in ng2: Forms

  • The async pipe lets us bind text or other elements to observables
<ul>
    <li *ngFor="#video of videos | async">{{video | json}}</li>
</ul>

RxJS in ng2: Http

  • Http is the ng2 replacement of $http and now returns an observable instead of a promise
 ngOnInit() {
    this.getAccounts();
  }
 
  getAccounts() {
    this.http.get('/accounts')
      .map((res:Response) => res.json()) //map to JSON
      .subscribe(
        //onNext, receives HTTP response, gets data in one shot
        data => { this.accounts = data}, 
        //onError (optional), if an error code is received
        err => console.error(err),
        //onComplete (optional and less useful), all data returned
        () => console.log('done')
      );
  }

Use forkJoin to combine requests

getProjectsAndAccounts() {
    Observable.forkJoin(
        this.http.get('/projects').map((res:Response) => res.json()),
        this.http.get('/accounts').map((res:Response) => res.json())
    ).subscribe(  //We don't subscribe to the individual observables
      data => {   //Runs after all calls complete
        this.projects = data[0]
        this.accounts = data[1]
      },
      err => console.error(err) //Runs if either call errors
    );
  }

Now put it in a service...

import {Injectable} from 'angular2/core';
import {Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Rx';
 
@Injectable()
export class ProjectService {
 
  constructor(private http:Http) { }
 
  getProjectsAndAccounts() {
    return Observable.forkJoin(
      this.http.get('/projects').map((res:Response) => res.json()),
      this.http.get('/accounts').map((res:Response) => res.json())
    );
  }
 
}

...and the rest into the component

import {ProjectService} from './project.service';

@Component({ 
//stuff and things
}) 
export class ProjectComponent {
  constructor(private _projectService: ProjectService){}

  getProjectsAndAccounts(){
      this._projectService.getProjectsAndAccounts().subscribe(
          data => {
              this.projects = data[0];
              this.accounts = data[1];
          }
      );
  }
}

Typeahead Demo!

(shamelessly stolen from Rob Wormald)

A Tip and Where to Go from Here

  • Pay attention to import statements in your app file and elsewhere
  • Look into Redux and ngrx/store
  • Check out these references...

References/Further Reading

Go forth and stream!

Intro to RxJS & Observables in Angular 2

By Sam Julien

Intro to RxJS & Observables in Angular 2

An overview of RxJS used in Angular 2.

  • 2,793