Introduction to RxJS and using it properly in Angular

Backgroud

  • Programming with asynchronous data streams
  • Improves user experience (handling real-time events  and interactive experience) 

What is Reactive Programming?

Need of observables

getData(() => {
   getMoreData(() => {
     getEvenMoreData() => {

     }
   })
})
getData()
    .then(getMoreData)
    .then(getEvenMoreData)

Callback hell

Promises

  • Can emit multiple values
  • Can be cancelled
  • Can Retry
  • Are Lazy

RxJS: The ReactiveX library for JavaScript

 

  • API developed by http://reactivex.io/
  • Gives support for asynchronous programming

    with observable streams

Observables

  • Can define both the setup and teardown aspects of asynchronous behavior

  • Has a subscribe method subscribe(fnValue, fnError, fnComplete)

let stream$ = Rx.Observable.create((observer) => {
    observer.next(1);
    observer.error('error message');
    observer.complete();
});

stream$.subscribe((data) => {
    console.log('Data', data);
},
(error) => {
    console.log('Error', error);
}

// 1

Always clean up

Unsubscribe

  • To avoid memory leaks 
  • Can be done explicitly stream$.unsubscribe()
  • Can be done implicitly with stream$.takeWhile()

Angular

  • Best time to unsubscribe is when your component is destroyed.
  • Do it inside ngOnDestroy()

Creating observables

.fromEvent()
.from()
.fromPromise()

we can create observable from any type of data stream

const btnStream$ = Rx.Observable.fromEvent(btn, 'click');
const arrayStream$ = Rx.Observable.from([1,2,3]);
const promiseStream$ = Rx.Observable.fromPromise(myPromise);

Operators

  • Creation type operator
  • Synchronous in nature

of

let stream$ = Rx.Observable.of(1,2,3,4,5)

filter

  • Filters out values from being emitted
let stream$ = 
Rx.Observable
.of(1,2,3,4,5)
.filter((value) => {
  return value % 2 === 0;
})

// 2,4

​Used to debug observables

do

let stream$ = Rx.Observable
.of(1,2,3,4,5)
.do((value) => {
  console.log('do',value)
})
.filter((value) => {
  return value % 2 === 0;
})

stream$.subscribe((value) => {
  console.log('value', value)
})

//  do: 1,do : 2, do : 3, do : 4, do: 5 
// value : 2, 4

Other commonly used operators

//delay()

var start = new Date();
let stream$ = Rx.Observable.interval(500).take(3);

stream$
.delay(300)
.subscribe((x) => {
    console.log('val',x);
    console.log( new Date() - start );
})

//0 800ms, 1 1300ms,2 1800ms

//sample
const btn = document.getElementById('btnIgnore');
var start = new Date();

const input$ = Rx.Observable
  .fromEvent(btn, 'click')

  .sampleTime(2000);

input$.subscribe(val => {
  console.log(val, new Date() - start);
});
//debounceTime()

const input = document.getElementById('input');

const example = Rx.Observable
  .fromEvent(input, 'keyup')
  .map(i => i.currentTarget.value);

//wait 0.5s, between keyups, throw away all other values
const debouncedInput = example.debounceTime(500);

const subscribe = debouncedInput.subscribe(val => {
  console.log(`Debounced Input: ${val}`);
});

Subject

Type of subjects:

Replay Subject

 

new Rx.ReplaySubject([bufferSize], [windowSize], [scheduler]);
let replaySubject = new Rx.ReplaySubject( 2 );

replaySubject.next( 0 );
replaySubject.next( 1 );
replaySubject.next( 2 );


let replaySubscription = replaySubject.subscribe((value) => {
    console.log('replay subscription', value);
});

//  1, 2

Async Subject

 

let asyncSubject = new Rx.AsyncSubject();
asyncSubject.subscribe(
    (value) => console.log('async subject', value),
    (error) => console.error('async error', error),
    () => console.log('async completed')
);

asyncSubject.next( 1 );
asyncSubject.next( 2 );
asyncSubject( 3 )
asyncSubject.error('err')
asyncSubject.complete()

// will emit 'err' as the last action

Behaviour Subject

 

let behaviorSubject = new Rx.BehaviorSubject(42);

behaviorSubject.subscribe((value) => console.log('behaviour subject',value) );
console.log('Behaviour current value',behaviorSubject.getValue());
behaviorSubject.next(1);
console.log('Behaviour current value',behaviorSubject.getValue());
behaviorSubject.next(2);
console.log('Behaviour current value',behaviorSubject.getValue());
behaviorSubject.next(3);
console.log('Behaviour current value',behaviorSubject.getValue());

// emits 42
// current value 42
// emits 1
// current value 1
// emits 2
// current value 2
// emits 3
// current value 3

Hot n cold Observables

let obs = Rx.Observable
.create(observer => observer.next(Date.now()));

obs.subscribe(v => console.log("1st: " + v));
obs.subscribe(v => console.log("2nd: " + v));

// 1st subscriber: 1465990942935
// 2nd subscriber: 1465990942936
  1. creates the producer
  2. activates the producer
  3. starts listening to the producer
  1. shares a reference to a producer
  2. starts listening to the producer

Cold observable

Hot observable

let obs = Rx.Observable
.create(observer => observer.next(Date.now()))
.publish();

obs.subscribe(v => console.log("1st: " + v));
obs.subscribe(v => console.log("2nd: " + v));

obs.connect();

// 1st subscriber: 1465990942936
// 2nd subscriber: 1465990942936

RxJS in Angular

 

Passing observables to the view

import { Component } from '@angular/core'
import { Observable } from 'rxjs/Rx'

@Component({
  selector: 'my-app',
  template: `
    <ul>
      <li *ngFor="let item of items | async">

      </li>
    </ul>
  `
})
export class AppComponent {
  public items = Observable.of([1, 2, 3])
}

RxJS in Angular

 

Using with http

import { Injectable } from '@angular/core'
import { Http } from '@angular/http'
import { Observable } from 'rxjs/Rx'
import 'rxjs/add/operator/map'

@Injectable()
export class RepoService {
  constructor(private _http: Http) {}

  getReposForUser(user: string): Observable<any> {
    return this._http
      .get(`https://api.github.com/users/${user}/repos`)
      .map((res: any) => res.json())
  }
}

RxJS in Angular

 

Communication between components

import { ChangeDetectionStrategy, Component, Input } from '@angular/core'

@Component({
  selector: 'my-score',
  template: 'Summary: ',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ScoreComponent {
  @Input() public score: number
}
@Component({
  selector: 'my-app',
  template: `
    <button (click)="counter$.next(1)">
      Up Vote
    </button>
    <my-score [score]="counter$ | async"></my-score>
  `,
})
export class AppComponent {
  public counter$: Observable<number> = new Subject<number>()
    .scan((acc: number, current: number): number => acc + current)
}

Thank you :)

RxJS using it properly in Angular

By Zenab Saleem

RxJS using it properly in Angular

  • 857