Advanced Observable Patterns in Angular apps with RxJS

Contents
Part 1 - Reactive programming and RxJS
- Creating observables
- Operators
- Higher order observables
- Error-handling
Part 2 - Observables in Angular
- EventEmitters
- Http
- Forms
- Router
INTRODUCTION
Reactive Programming
// concert spectators
let inMainStand = 300;
let inVipStand = 50;
// code...
let total = inMainStand + inVipStand; // 350
// code...
inMainStand += 60;
console.log(total); // total = ?
total = inMainStand + inVipStand; // 410
- imperatively recalculate the value of total
Reactive Programming

Reactive Programming
Product
Vendor
Cart
Cupon
Sale
Payment
User profile
Invoice
Reactive Programming
Cart
Invoice
Add product...
...update the total
Reactive Programming
Cart
Invoice
import {Invoice} from './invoice';
class Cart {
constructor(invoice: Invoice){ ... }
addProduct(product) {
...
this.invoice.update(product); // needs to be public
...
}
}
Passive
Proactive
Reactive Programming
Cart
Invoice
import {Cart} from './cart';
class Invoice {
constructor(cart: Cart){ ... }
init() {
...
this.cart.onCartChange( callback )
}
}
Reactive
Listenable
Reactive Programming
Cart
Cupon
Sale
Payment
Invoice
- What does it affect? - look inside
- How does it work? - find usages
Passive programming
- remote setters and updates
Reactive Programming
Cart
Cupon
Sale
Payment
Invoice
- What does it affect? - find usages of events
- How does it work? - look inside
Reactive programming
- events, observation and self updates
Reactive Programming
Interruptions in web applications
- API events
- WebSockets
- User-triggered events
- Timers (setTimeout, setInterval)
Reactive Programming
(Global) Event Bus

Reactive Programming
Observer pattern
Image credits:
https://www.dofactory.com/javascript/observer-design-pattern

Reactive Programming
Observer vs PubSub

Reactive Programming
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
Reactive Programming
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
generators
Reactive Programming
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
promises
generators
Reactive Programming
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
promises
generators
observables
OBSERVABLES
Special credits
Michael Hladky
@Michael_Hladky
- marble diagrams
- inspirational talks
Ben Lesh
@BenLesh
inspirational talks -
work on RxJS -


OBSERVABLES
- A stream of data ( 0 or more values of any type)
- Pushed over any amount of time (can end, but not necessarily)
- Cancelable ( can be stopped from emitting a value - unsubscribed)
- Lazy - won't emit values until we subscribe to them
- Just a function that takes an observer and returns a function

OBSERVABLES
Dealing with async in our apps:
- Dom events
- Animations
- AJAX
- WebSockets
- Server Events
AJAX with callbacks
getData(function(successResult) {
// do something with the data
});
getData(
function(successResult) {
// do something with the data
},
function(faliureError) {
// handle the error
},
);
AJAX with callbacks
let result = fetch('api/users.json');
result
.then(success => {
// handle success
})
.catch(error => {
// handle error
})
AJAX with promises
result
.then(...)
.then(...)
.then(...)
Observables
Unifying callbacks, promises and event handlers
Shape of an observable:
- A function
- accepts an observer ( an object with `next`, `error` and `complete` methods on it)
- returns a cancellation function
Creating an Observable
let observable$ = new Observable(() => { })
Observable
- read-only - consumer
- plain function
- exposes 3 channels: next, error and complete
excersise file: https://stackblitz.com/edit/adv-rxjs-1
Creating an Observable
let observable$ = new Observable(observer => { observer.next(1); observer.next(2); })
Observer
- write-only - producer
- instance passed to observable
- provide next, error and complete methods
- just an interface
Creating an Observable
let observable$ = new Observable(observer => { observer.next(1); observer.next(2); }) observable$.subscribe(value => { console.log(value) }
Subscription
- triggers the observable execution
- returns an unsubscribe() method that stops the observable
Creating an Observable
let observable$ = new Observable(observer => { observer.next(1); observer.next(2); return () => { // cleanup resources when done }; }) const subscription = observable$.subscribe(value => { console.log(value) }) subscription.unsubscribe(); // stop emitting values
Creating an Observable
let observable$ = new Observable(observer => { observer.next(1); observer.next(2); observer.error(new Error('Bad')); }) const subscription = observable$.subscribe( value => { console.log(value)}, error => { console.log(error.message)} )
Creating an Observable
Creation functions
-
of(value1, value2, value3)
-
from(promise/itterable/observable)
-
fromEvent(target, eventName)
-
interval(time)
-
timer(time)
Creating a promise
let promise = new Promise((resolve, reject) => { doAsyncThing((err, value) => { if (err) { reject(err); } else { resolve(value) } }) }) promise.then(successFn, errorFn);
Creating an observable
let promise = new Promise((resolve, reject) => { doAsyncThing((err, value) => { if (err) { reject(err); } else { resolve(value) } }) }) promise.then(successFn, errorFn);
Hot vs Cold observables
COLD is when your observable creates the producer
// COLD var cold = new Observable((observer) => { var producer = new Producer(); // have observer listen to producer here });
-
producer is created and activated duringsubscription - unicast => everyone gets their own instance
- observables are "cold" by default
Hot vs Cold observables
-
HOT is when your observable closes over the produce
// HOT var producer = new Producer(); var cold = new Observable((observer) => { // have observer listen to producer here });
- producer is created and activated outside and independent of subscription
- multicast => shared reference to the producer
Subjects
Observables are unicast - each subscriber manages its own execution context
Subjects are multicast observables - values are multicasted to many Observers
Types of subjects
- BehaviorSubject - has the notion of "current value"
- ReplaySubject - can record part of it's execution
Operators
Observables are collections of pushed values or events that we can:
- query (filter)
- transform (map)
- accumulate (reduce)
- join
- flatten
- more...
Array functions
let array = [1,2,3,4,5];
array
.map(v => v * v)
.filter(v => v > 5 && v < 20)
.reduce((acc, curr) => acc + curr, 0)
// 25
Array functions

Operators
let observable$ = from([1,2,3,4,5]);
observable$
.pipe(
map(v => v * v),
filter(v => v > 5 && v < 20),
reduce((acc, curr) => acc + curr, 0)
)
.subscribe(val => console.log(val))
// 25
excersise file: https://stackblitz.com/edit/adv-rxjs-2
map

filter

scan

reduce

tap

Custom operators
const toUpperCase = (source: Observable<any>) => { return new Observable((observer) => { return source.subscribe({ next(x) { observer.next( x.toUpperCase() ) } }) } )} from('hello') .pipe(toUpperCase) .subscribe(val => l(val))
excersise file: https://stackblitz.com/edit/adv-rxjs-3
Custom operators
const pow = (p = 1) => (source: Observable<any>) => { return new Observable((observer) => { return source.subscribe({ next(val) { observer.next( Math.pow(val, p) ) } }) } )} from([1, 2, 3]) .pipe(pow(3)) .subscribe(val => l(val))
Operators can take parameters as well
Stopping streams

Higher order observbles
const buttonObs$ = fromEvent(querySelector('button'), 'click');
buttonObs$.subscribe(() => {
http$.get('/api/users').subscribe( data => {
// handle loaded data
})
})
excersise file: https://stackblitz.com/edit/rxjs-adv-4
Flatning operators
mergeMap

Flatning operators
concatMap

Flatning operators
switchMap

Flatning operators
exhaustMap

Combination operators
zip

Combination operators
combineLatest

Combination operators
forkJoin

Combination operators
withLatestFrom

Error Handling
const obs$ = new Observable(observer => { observer.closed; // false observer.next(1); // emit 1 observer.complete(); observer.closed; // true observer.next(2); // won't emit });
- Completing a stream
excersise file: https://stackblitz.com/edit/rxjs-adv-5
Error Handling
- A stream can only error once
const obs$ = new Observable(observer => {
observer.closed; // false
observer.next(1); // emit 1
observer.error(new Error('Bad!'));
observer.closed; // true
observer.next(2); // won't emit
});
obs$
.subscribe({
next: v => console.log(v),
error: e => console.log(e.message)
});
Error Handling
- A stream can only error once
const obs$ = new Observable(observer => {
observer.next(1);
observer.error(new Error('BANG!'));
}).pipe(
catchError(err => {
console.log('Intercepted error:' + err);
return of('I got this');
})
)
obs$.subscribe(v => console.log(v));
// 1 'I got this'
Error Handling
- Recovering after an error - the retry operator
const getData$ = http.get('/api/users')
.pipe(
retry(3),
catchError(() => of('Something went wrong');
)
getData$.subscribe(value => console.log(value));
Schedulers
Schedulers control timing around when an event occurs
RxJS Schedulers
- queue - During same "job", but on a queue
- asap - Next "job", aka, "microtask"
- async - Next "timeout"
- animationFrame - Next requestAnimationFrame
excersise file: https://stackblitz.com/edit/rxjs-adv-6
RxJS in Angular
Observables in Angular
Angular power-up -> async pipe
Advanced Observable Patterns in Angular apps with RxJS
By Andrei Antal
Advanced Observable Patterns in Angular apps with RxJS
- 2,167