Use the Custom Operator Force

BECOME AN RXJS JEDI

Ryan chenkie

angular/node consultant

google developer expert

angularcasts.io

@ryanchenkie

Operators are what give you the power to be an RxJS Jedi but you need to know how to use the force

Operators

are

misunderstood

lots of them

Naming not always intuitive

unclear when/how to use together

what if it doesn't do what we need?

Your RxJS Jedi Training Begins Today

how observables work on the inside

operating on individual values

combining streams

accumulating streams

training plan

custom operators

materials

github.com/chenkie/custom-operators-workshop

5 mins

back
to
basics

what is an operator?

operators are the things you put in the way of your stream before getting values out of it

let's think about drinking water

Source Observable

Filter Operator

Combination Operator

Accumulation Operator

Final Output

operators
vs
js methods


const values = ['foo', 'bar', 'baz'];
const filteredValues = values.filter(v => v !== 'foo');

const valueStream = Rx.Observable.of('foo', 'bar', 'baz');
const filteredValueStream = valueStream.filter(v => v !== 'foo');

if we  have map and filter in javascript, why do we need them in rxjs?

on the inside, operators work on observables

operators subscribe to a source observable and return a result observable


function addHello(source) {
  return Rx.Observable.create(function(observer) {
    source.subscribe(
      function (value) { 
        observer.next(`Hello ${value}`);
      },
      function (err) {
        observer.error(err)
      },
      function () {
        observer.complete();
      }
    )
  });
}

rxjs filter operator


class FilterOperator<T> implements Operator<T, T> {
  constructor(private predicate: (value: T, index: number) => boolean,
              private thisArg?: any) {
  }

  call(subscriber: Subscriber<T>, source: any): TeardownLogic {
    return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg));
  }
}

class FilterSubscriber<T> extends Subscriber<T> {

  count: number = 0;

  constructor(destination: Subscriber<T>,
              private predicate: (value: T, index: number) => boolean,
              private thisArg: any) {
    super(destination);
  }

  ...
}

export class Subscriber<T> extends Subscription implements Observer<T> {

  ...

  next(value?: T): void {
    if (!this.isStopped) {
      this._next(value);
    }
  }


  error(err?: any): void {
    if (!this.isStopped) {
      this.isStopped = true;
      this._error(err);
    }
  }

  complete(): void {
    if (!this.isStopped) {
      this.isStopped = true;
      this._complete();
    }
  }
}

a closer
look at
native operators

individual value operators

combination operators

accumulation operators

let's look at three types

the
filter
operator

we apply a predicate to filter out certain values


public getUrl(): void {
  this
    .router
    .events
    .pipe(
      filter(event => event instanceof NavigationStart),
      filter((event: NavigationStart) => (/foo/).test(event.url))
    )
    .subscribe(event => {
      console.log(event.url);
  });
}

visualizing with marble diagrams

rxmarbles.com

Thanks André Staltz!

@andrestaltz

exercise

custom filter operator

your tasks

  • Check out the ex-1-start branch in the workshop repo
  • Create a custom operator which filters out odd values emitted from a source stream
  • Get the partially-completed filter-odd.js code from the workshop repo and work in JS Bin
  • HINT: Use Rx.Observable.create inside the custom operator

10 mins

Workshop Repo

github.com/chenkie/custom-operators-workshop

the
combinelatest
operator

we will often need results from multiple observable streams

in many cases, we need a combination of these values

all source observables need to emit at least one value

the latest values from each are combined together

combinelatest under the hood

calls subscribeToResult on all the observables passed in

keeps track of the values emitted from the supplied observables and remembers the latest ones

A projection function can optionally be supplied

exercise

using combinelatest in a reactive form

your tasks

  • Check out the ex-2-start branch in the workshop repo
  • Complete async validator code so that flight searches can be pre-validated for BadUXAir
  • Create references to the valueChanges streams from the form and combine the latest results before running the async validator

10 mins

Workshop Repo

github.com/chenkie/custom-operators-workshop

ACCUMULATING
STREAMS

values can be combined between streams but also within the same stream

the
scan

operator

accumulate values from a single observable stream

kinda like array.reduce

will give back intermediate values that have been accumulated

can be supplied a seed value

DEMO

scan operator

custom
operators

we don't want to repeat long/complex operator chains

we can abstract to a custom operator instead

two common patterns for custom operators:

put it on observable.prototype

return a function and supply a source observable

you can (and should) use rx operators inside your custom operators

(Don't reinvent the wheel)


const multiply = multiplyFn => (source: Observable<any>) => {
  return new Observable(observer => {
    return source.subscribe({
      next(x: any) {
        observer.next(multiplyFn(x));
      },
      error(err: any) {
        observer.error(err);
      },
      complete() {
        observer.complete();
      }
    });
  });
};

this.obs
  .pipe(multiply(x => x * 10))
  .subscribe(x => console.log(x));

exercise

route change reporter

your tasks

  • Check out the ex-3-start branch in the workshop repo
  • Supply the necessary RxJS operators inside the custom reportRouteChange operator
  • Pipe the custom operator before subscribing to route changes so that the previous and current routes can be reported
  • HINT: we need to use an Rx operator which gives us the last and current values emitted

15 mins

Workshop Repo

github.com/chenkie/custom-operators-workshop

may the rxjs force be with you

thank you!

@ryanchenkie

angularcasts.io

bit.ly/custom-operators

Made with Slides.com