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

Use the Custom Operator Force; Become an RxJS Jedi

By Ryan Chenkie

Use the Custom Operator Force; Become an RxJS Jedi

Let’s face it, doing advanced work with RxJS is just plain tough. Sure, some of the simpler operators are easy to grok, but once we get beyond simple maps and subscribes, it doesn’t take much to just give up and go back to where things are comfortable. This is a shame because there’s a whole world of streamy goodness that, for many developers, is just around the corner. Writing advanced RxJS requires that we know its operators. There’s arguably no better way to get to know them than by writing our very own. In this workshop we’ll dig into RxJS by crafting our own operators. This will be hugely beneficial for you because, to do so, we’ll really need to get to know some of the more advanced native operators. If you want to become an RxJS Jedi by getting to know RxJS inside and out, this workshop is for you.

  • 1,324