RxJS: Mastering the Operators

What are Operators?

  • Pure functions that transform information in the Observable stream.
  • Create a new Observable, often based on the current Observable.
  • Allow for complex asynchronous code to be easily composed in a declarative manner.
  • Methods of the Observable class.

Observable Generators

  • Rx.Observable.<operator>
  • Often create a new Observable without any input.

Instance Operators?

  • Rx.Observable.prototype.<operator>
  • The this scope is the input Observable.

Composable?

  • All operators return an Observable, making them chainable (or pipeable, but not lettable).
  • Subscribing to the output Observable will also subscribe to the input Observable.

Versions?

  • Stable release: 5.5.6
  • Alpha release: 6.0.0-alpha.3

Can I import all operators

  • Yes, but you likely do not need all of them.
  • Be kind to your users.

Import a single operator?

import from "rxjs/add/operators/switchMap";
import { switchMap } from "rxjs/operators";

or

How do I chain operators in v5.4.x?

class PostsComponent {

  private user: Observable<User>;

  ngOnInit() {
    this.posts = this.user
      .map(user => user.id)
      .switchMap(id => 
        this.postsService.getPosts(id)
      );
  }
}

How do I pipe operators in v5.5.x?

class PostsComponent {

  private user: Observable<User>;

  ngOnInit() {
    this.posts = this.user.pipe(
      map(user => user.id),
      switchMap(id => 
        this.postsService.getPosts(id)
      )
    );
  }
}

Ok, what about Angular?

  • Angular <3 Observables
  • Asynchronous tasks in Angular return an Observable.
  • The AsyncPipe is your friend.

How many?

75!

How do I choose?

Overview

  • Lazy-loaded modules
  • Angular Material
  • CoreModule for interceptors, models and services
  • StateModule with NgRX actions, effects and reducers
  • Stateful (smart) containers
  • Stateless (dumb) components
git checkout ngrx-refactor-2

cd server && yarn serve

cd client && ng serve
https://github.com/blove/ngrx-tour-of-heros

json-server

cd server

yarn serve

Let's dive in.

map()

  • One of top 5 operators in use
  • Data stream transformation operator
  • Akin to Array.prototype.map

map()

src/app/+powers/containers/power/power.component.ts

filter()

  • One of top 5 operators in use
  • Data stream filtration operator
  • Akin to Array.prototype.filter

filter()

src/app/+heroes/components/add-hero-dialog.component.ts

src/app/+powers/containers/power/power.component.ts

tap()/.do()

app/+powers/containers/edit/edit.component.ts

  • One of top 5 operators in use
  • Allows stream to be observed sideband
  • Can perform side effects with observed data
  • Does not modify the stream in any way
  • Can also observe error and complete
  • Useful for doing work that reacts to the stream but otherwise does not handle the stream

tap()/.do()

app/+powers/containers/edit/edit.component.ts

switchMap()

src/app/+heroes/containers/edit/edit.component.ts

  • One of top 5 operators in use
  • Another transformation operator
  • Switches from one stream to another
  • Unsubscribes from previous Observable
  • Subscribes to new Observable

src/app/state/powers/effects/powers.ts

switchMap()

  • One of top 5 operators in use
  • Another transformation operator
  • Switches from one stream to another
  • Unsubscribes from previous Observable
  • Subscribes to new Observable

catchError()/.catch()

app/state/powers/effects/powers.ts

first()

app/+powers/containers/edit/edit.component.ts

  • Returns the first value observed and completes stream
  • Another filtration operator
  • Allow a predicate to be used to filter for first desired value
  • Allows a selector to be used to transform returned value
  • Supports optional default value
  • Useful for once-and-done handlers

first()

app/+powers/containers/edit/edit.component.ts

last()

  • Returns the last value observed and completes stream
  • Another filtration operator
  • Allow a predicate to be used to filter for first desired value
  • Last will wait for completion from original stream before returning
  • Useful for once-and-done handlers

last()

take()

  • Returns the first N values observed and completes stream
  • Another filtration operator

take()

skip()

  • Skips the first N values observed then continues to emit any additional values
  • Another filtration operator
  • Often used with Replay and Behavior subjects to skip unwanted values
  • Synonymous with .filter((val, index) => index >= N)

skip()

startWith()

  • Emits specified value or values into the stream first
  • Continues emitting values from source observable
  • Accepts optional scheduler for scheduling emissions

startWith()

withLatestFrom()

  • Includes latest value from another Observable once both have emitted only when source emits
  • Provides both values (from source and other observables) in array argument
  • Can use project function to transform
  • Simplest way to join streams of data
  • .subscribe(([orig, latest]) => {...})

withLatestFrom()

src/app/+powers/containers/power/power.component.ts

distinctUntilChanged()

src/app/+heroes/components/add-hero-dialog.component.ts

  • Only emits distinct values from source
  • Cross between filtration and rate limiting operator
  • Allows comparator function to be used to determine uniqueness
  • Uses strict equality check by default if no comparator provided
  • Companions: distinct(), distinctUntilKeyChanged()

src/app/+powers/components/edit-power-dialog.component.ts

distinctUntilChanged()

src/app/+heroes/components/add-hero-dialog.component.ts

  • Only emits distinct values from source
  • Cross between filtration and rate limiting operator
  • Allows comparator function to be used to determine uniqueness
  • Uses strict equality check by default if no comparator provided
  • Companions: distinct(), distinctUntilKeyChanged()

src/app/+powers/components/edit-power-dialog.component.ts

debounceTime()

  • Only emits values once a given interval
  • Data stream rate limiting operator
  • Drops previous value if new value arrives before interval
  • Defaults to milliseconds
  • Allows scheduler to be supplied to configure what the dueTime is interpreted as (if you need other than milliseconds)

debounceTime()

src/app/+heroes/components/add-hero-dialog/add-hero-dialog.component.ts

concat()

  • Concatenates any number of observables in order
  • Data stream creation operator
  • If any observable does not complete, subsequent are never subscribed to
  • Allows scheduler to be supplied to control emission of notifications

concat()

forkJoin()

src/app/+heroes/containers/character/character.component.ts

  • Forks by starting multiple observers at once
  • Joins final value from each observable when they complete
  • Data stream creation operator
  • If any input observable never completes, forkJoin never completes

reduce()

  • Accumulate or transform values upon completion notification
  • Data stream transformation operator
  • Can be used to manage & maintain state
  • Like Array.prototype.reduce
  • See scan() for streaming version

reduce()

scan()

  • Reduce data over time
  • Data stream transformation operator
  • Can be used to manage & maintain state
  • Accumulate over time
  • Transform over time
  • Like Array.prototype.reduce, except streaming and continuous
  • See reduce() for non-streaming version

scan()

Thanks!

  • Jon Rista @ BrieBug
  • Ben Lesh
  • Angular Community

Mastering the Operators

By Brian Love

Mastering the Operators

Learn to master many of the RxJS operators that enable Angular developers to code reactively.

  • 1,119