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 Observables, query 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
- Official RxJS Docs
- My resource-gathering posts on FP and Observables
- RxJS Koans
- RxMarbles
- Functional Reactive Programming for Angular 2 Developers
- Reactive Data Flow in Angular 2
- Angular 2: HTTP, Observables, and concurrent data loading
- Egghead.io coureses on RxJS, async, and Redux (excellent)
Go forth and stream!
Intro to RxJS & Observables in Angular 2
By AdapTeach
Intro to RxJS & Observables in Angular 2
An overview of RxJS used in Angular 2.
- 1,749