Angular2 + rxjs

91
Angular 2 with rxjs chris noring Frontend Developer OVO Energy, London Google Developer Expert

Transcript of Angular2 + rxjs

Page 1: Angular2 + rxjs

Angular 2 with rxjschris noring

Frontend Developer OVO Energy, LondonGoogle Developer Expert

Page 2: Angular2 + rxjs

Who am I@chris_noring

Page 3: Angular2 + rxjs

Why angular 2

Page 4: Angular2 + rxjs

Backed by google

It’s released !!

It’s fast

It uses typescript

It uses components

compile time type checkinggood when teams and codebase grows

Fewer concepts to learn than angular 1

Mobile first Designed to be memory efficient and less CPU cycles

Faster than React, less fast than Vue.js

Tooling like angular-cli

1

2

3

4

5

6

7

Page 5: Angular2 + rxjs

Bootstrapping ng-1index.htmlapp.js

ng-app =“app”

angular.module(‘app’, [ dep1, dep2, dep3]).controller().service().factory().filter()

1

2

Page 6: Angular2 + rxjs

Bootstrapping ng 2main.t

s

app.module.ts

platform.bootstrapModule(AppModule);

app.component component1 component n..

module1 module1

1

2define dependant modulesdefine components our module consist ofdefine services

bootstrap app

Page 7: Angular2 + rxjs

Filter Constant/ Value

@Pipe

Service, factory, provider

vanilla javascript

ng1

ng2

Less conceptsfilter is faster, more js vanilla

more performant

.constant.value

.factory,

.service, .provider

Page 8: Angular2 + rxjs

Module Router

ngModuleMuch more

powerful

ng1

ng2

Less conceptsmodules + better router

lazy loadingbuilt for > 100k routes

better separation, itsnot all global like ng1

Page 9: Angular2 + rxjs

Controller Directiveelement

@Component @Directive

. Directive

Attrib

Less concepts

. DirectiveStructural

Page 10: Angular2 + rxjs

Bindings

Page 11: Angular2 + rxjs

Bindings - one way

<div [attr] =“prop” > </div>export class SomeComponent{ prop:string;}

{{ prop }}

[ someAttribute ]

Page 12: Angular2 + rxjs

Event binding<div (click)=“method()”>

export class SomeComponent{ method(){ // do something } }

(event)=“method()”

Page 13: Angular2 + rxjs

Banana in a box, 2-way

<input [(ngModel)]=“user.name”>

export class SomeComponent{ user:User; }

shorthand for

class User{ name:string;}

banana in a box

[(ngModel)]=“dataProperty”

<input [value]=“user.name" (input)=“user.name = $event.target.value">

Page 14: Angular2 + rxjs

Directives

Page 15: Angular2 + rxjs

3 directive typesStructural

Attribute

Component

@Directive({})export class SomeClass{

}

@Component({})export class SomeComponent{}

Page 16: Angular2 + rxjs

Structural directiveschanges the DOM layout

by adding and removing DOM elements

<div *ngIf=“condition”> some text..<div/>

add or remove from DOM tree

<div *ngFor=“let item of items”>{{ item.prop }}</div><div [ngSwitch]="status"> <template [ngSwitchCase]=“'beginner'">Young padawan</template> <template [ngSwitchCase]="'expert'">Jedi</template> <template ngSwitchDefault>Unknown</template></div>

create one tow for each items

select one matching case

Page 17: Angular2 + rxjs

Attribute directivechanges the appearance

or behavior of a DOM element

import { Directive, ElementRef, Input, Renderer } from '@angular/core';

@Directive({ selector: '[myHighlight]' })export class HighlightDirective { constructor(el: ElementRef, renderer: Renderer) { renderer.setElementStyle(el.nativeElement, 'backgroundColor', 'yellow'); }}

2 Add to module

3 use in component template

@NgModule({ declarations : [ HighLight ]})

<div myHighLight > highlight me!! </div>

1 Define directive

Page 18: Angular2 + rxjs

Component- Replaces controller and element directive from ng1

- Can be routed to, is the page

- Uses a decorator on a class to turn the class into a component

myDirective

my-directive

component-name

component-name:D

ng1 naming is gone

- Can be used as component on a page, part of the page

Page 19: Angular2 + rxjs

First component@Component({ selector : ‘component-name’, template : ` <div> content </div> `, otherprops..})export class SomeComponent{}

<component-name></component-name>

Config object

Class is decorated

Use component

Page 20: Angular2 + rxjs

Componentbind value/methods to itInput

Output<my-component [user]=“user” (save)=“save( $event )” ></my-component>

export class ParentComponent{ save(ev){ console.log( ‘child called save..’ ,ev.value ); }}

export class ChildComponent{ @Input() user:User; @Output() save = new EventEmitter();

persist(){ this.save.emit({ value : ’data’ }); } }

<div> Here is the user {{ user.name }} <input [(ngModel)]=“user.name” > <button (click)=“persist()” >Save</button></div>

Emit data

Receive data

Send in event1

2

3

Page 21: Angular2 + rxjs

Pipes

Page 22: Angular2 + rxjs

Pipes<div *ngFor=“let item of items | pipe:arg”></div>

export class SomePipe implements PipeTransform { value: Array<Type> = []; transform(items: Array<Type>, arg: boolean) { if (items && items.length) { this.value = items.filter((item: Type) => { return item.property == arg; }); } return this.value; }}

@Pipe({ name: "pipe"}) 1

3 return matching item

implement interface

2

implement transform

Page 23: Angular2 + rxjs

Services

Page 24: Angular2 + rxjs

Service - transientWe DONT care about state

Ex utility service

export class Service { static calcSomeThing(){ return 1+1; }}

import { Service } from ‘utilities/service’;

export class SomeComponent{ doStuff(){ let answer = Service.calcSomething(); }}

Consume service, in componentDeclare service

Page 25: Angular2 + rxjs

Service - singleton

export class Service { getData(){ }}

@injectable()

import { Service } from ‘services/service’;

export class SomeComponent{

construcor( private srv:Service, private other:OtherService){

}

doStuff(){ var data = this.srv.getData(); }}

We care about stateDeclare Consume

Page 26: Angular2 + rxjs

Moduleswiring up the applications

Page 27: Angular2 + rxjs

ngModule

What modules am I dependent onWhat Components do I containBootstrap / Export if root module or feature moduleServices I can inject

123

@NgModule({ imports: [ BrowserModule, HttpModule ], declarations: [ AppComponent, DataComponent ], bootstrap/exports: [ AppComponent ], providers : [ srv1, srv2, srv3 ]})export class AppModule { }

1

2

3

4

4

Page 28: Angular2 + rxjs

What is a module?Logical division of code

Consist of

Other modules Directives ServicesOne root component = entry point for the module

You can and you should write your own module to make your app easier to work with

Remember

OROne/ many exported component/s = that other modules may use

Page 29: Angular2 + rxjs

Applicationmodule overview

Application module

Feature module

Commonmodule

Dependant on

Dependant on

Containsfeauture directives

feauture service

feature pipe

application wide

service

common component

common service

common pipe

bootstrap

exports

exports

Page 30: Angular2 + rxjs

Application module overviewRoot

module

Feature

module

Common

module

@NgModule({ imports : [ CommonModule ], exports : [ FeatureComponent ], declarations : [ FeatureComponent, Comp1, Comp2.. ], providers : [ moduleService ]})export class FeatureModule {}

@NgModule({ imports: [ FeatureModule BrowserModule ], declarations: [ AppComponent, Pipe, Directive ], bootstrap: [ AppComponent ], providers : [ globalService ]})export class AppModule {

}

Dependant on

Start app with

Consists of

Injectable srv

1

2

Page 31: Angular2 + rxjs

Framework modules

NgIf NgFor

imports

Browser module

Common

modulethat defines

Formsmodule

ReactiveForms module

OR

Page 32: Angular2 + rxjs

Lifecycle hooksComponent lifecycle

Page 33: Angular2 + rxjs

constructor

ngOnChanges

ngOnInit

ngDoCheck

ngAfterContentInit

ngAfterContentChecked

ngAfterViewInit

ngAfterViewChecked

ngOnDestroy

There are interfaces definedthat lets you hook into these events

Lifecycle events for a component

Page 34: Angular2 + rxjs

ngOnInit, ngOnDestroyclass SomeComponent implements OnInit {

ngOnInit(){ // setup things like fetch init data, // listen to events etc.. }}

class SomeComponent implements OnDestroy { ngOnDestroy(){ // cleanup, remove event listeners etc. }}

Page 35: Angular2 + rxjs

ngOnChangeswhen values are bound to a component

class Component implements OnChanges{ @Input() data;

ngOnChanges(changes: {[propertyName: string]: SimpleChange}) { for (let propName in changes) { let chng = changes[propName]; let cur = JSON.stringify(chng.currentValue); let prev = JSON.stringify(chng.previousValue); }

}}

Page 36: Angular2 + rxjs

First projectapp with a few components and a service

show a list of things

Page 37: Angular2 + rxjs

App component

List component

main.ts

app.module.ts

index.html

@NgModule({ imports : [ BrowserModule ], bootstrap : [ AppComponent], declarations : [ AppComponent, ListComponent ], providers : [ ]})export class AppModule {}

Put our components in declarations so they know of each other

NO services yet

AppComponents is our entry point

Page 38: Angular2 + rxjs

app.component.ts

@Component({ selector : ‘app’, template : ‘<list-component></list-component>’ })export class AppComponent{

}

App component

Page 39: Angular2 + rxjs

@Component({ selector : ‘list-component’ template : `

<div *ngFor=“let person of persons”> {{ person.name }} <button (click)=“select( person )” >select</button></div><div *ngIf=“selectedPerson” > Selected: {{ selectedPerson.name }}</div>`})export class ListComponent { persons:Array<Person>; selectedPerson:Person; constructor(){ this.persons = [{

name : ‘Darth Vader’},{ name : ‘Luke Skywalker’}, { name : ‘Yoda ’}]

}

select( person:Person ){ this.selectedPerson = person; }

}

List componentrepeater

show if exist

Page 40: Angular2 + rxjs

Data fetchingFetch API or Rxjs

ProductServic

e

Http Servic

eWebServerGET GET

Promise or Observable

Page 41: Angular2 + rxjs

Fetching data with fetch api and promise

fetch(‘data.json',init).then(function(response) { return response.blob();})

var init = { method: 'GET', headers: myHeaders, mode: 'cors', cache: 'default' };

There is always this one if you don’t want to use Observables

Page 42: Angular2 + rxjs

import { Http } from ‘@angular/http’

@Injectable()class Service{ constructor( private http:Http ) {

}

getData(){ return this.http.get(url); } }

Http client in angular 2export class Component{ data;

constructor( private service:Service ){ }

ngOnInit(){ this.service .getData() .map( res:Response => res.json() ) .subscribe( json => this.data = json, err => console.log(err) ) }}

import ‘rxjs/add/operator/map’

Page 43: Angular2 + rxjs

ImportsSo when dealing with fetching data ensure you import:

The needed operator import, one for each import

import ‘rxjs/add/operator/operator’ operator = do, map, catch etc..

import { Observable } ‘rxjs/Observable’

import { Http, Response } ‘@angular/http‘

Common classes

Page 44: Angular2 + rxjs

From promise to Observable

Page 45: Angular2 + rxjs

Problems with promise

Not cancellable

We need to write a lot of code to retry our logic, becomes messy

Only returns one value

Not easy to compose with other things like callbacks and event etc, although them being async in their nature

Cumbersome to retry

Page 46: Angular2 + rxjs

A better way, observables

stream of values over time

1 2 3 4 5 6

Page 47: Angular2 + rxjs

Observablebreakdown

var stream = new Rx.Observable.create( fnSuccess,fnError,fnCompleted )

stream.subscribe((data) => { console.log( “Data”,data );})

nothing happens till someone subscribes

Page 48: Angular2 + rxjs

Observableunder the hood

var stream = Rx.Observable.create(function(observer){ observer.onNext(1); observer.onNext(2); observer.onNext(3); observer.onError( ‘there is an error’ )})

stream.subscribe( function(data){ console.log( data ); }, function(error) { console.error( error ); })

emit values

report error

1

1

2

2

Page 49: Angular2 + rxjs

Observablecleaning up

var homemadeStream = Rx.Observable.create((observer) => { var i=0;

var handle = setInterval(function() { observer.onNext( i++ ); }, 500);

return function(){ console.log('Disposing timeout'); clearTimeout( handle ); }});

var subscription2 = homemadeStream.subscribe((val) => { console.log('Homemade val',val);});

setTimeout(function() { console.log('Cancelling homemadeStream'); subscription2.dispose();}, 1500);

define dispose behaviour

Call dispose

Page 50: Angular2 + rxjs

Observableturning something into an

observablevar stream = Rx.Observable.create(function(observer){ var request = new XMLHttpRequest();

request.open( ‘GET’, ‘url’ ); request.onload = function(){ if(request.status === 200) { observer.onNext( request.response ); observer.onCompleted(); } else { observer.onError( new Error( request.statusText ) ) } } request.onerror = function(){ observer.onError( new Error(‘unknown error’) ); } request.send();})

1

2

3

3

stream.subscribe( (result) => { console.log( result );

} err => {}() => {})

132

Page 51: Angular2 + rxjs

Rich compositionCreate an observable from different types of sourcesUse operators to format the observable output

Page 52: Angular2 + rxjs

Everything is a stream

Everything can be turned into a a streamalmost

click events

key events

data from

server

callbacks

socket

Page 53: Angular2 + rxjs

DONT create observables by hand

Page 54: Angular2 + rxjs

Different ways to create an observable

Rx.Observable.fromArray([ 1,2,3,4 ])

Rx.Observable.fromEvent(element, ‘event’);Rx.Observable.fromArray(eventEmitter, ‘data’, function(){})

Rx.Observable.fromNodeCallback(fs.createFile)

Rx.Observable.fromCallback(obj.callback)

Rx.Observable.range(1,3)Rx.Observable.interval(miliseconds)

Page 55: Angular2 + rxjs

There is an operator for that

Page 56: Angular2 + rxjs

var stream = Rx.Observable.interval(500).take(5).filter((val) = > { return val % 2 === 0;}).map((val) => { return “val” + 1;})

Operators

stream.subscribe(() => {})

operator is a function that returns an observable

emit value every 500 ms

limit number of values

operator: change the value

operator: only emit certain values

Page 57: Angular2 + rxjs

Operatorsoverview

120+ operators ( 60+ Rxjs5 )

Combination Conditional

Creational

Error handling

Filtering

Transformation

Utility

Categories

Page 58: Angular2 + rxjs

How am I gonna keep track of that?

Page 59: Angular2 + rxjs

Start with these operators

map

filter

flatMap

switchMap

do

merge

of

interval

take

Page 60: Angular2 + rxjs

Marble diagramsconcat

Stream

Other stream

Resulting stream

1

Most operators are covered at rxmarbles.com

2 3

4 5

1 2 3 4 5

stream/s + operation = new stream

concat

Page 61: Angular2 + rxjs

Marble diagramsconcat

Stream

Resulting stream

1

Most operators are covered at rxmarbles.com

2 3

1 2 3

stream/s + operation = new stream

delay

Page 62: Angular2 + rxjs

flatMapUnderstand

Page 63: Angular2 + rxjs

flatMapRx.Observable.of(1,2,3).map( (val) => {

} ) return Rx.DOM.getJSON( ‘data’ + val +‘.json' )

Becomes a list of observables, hard to work with

Rx.Observable.of(1,2,3).flatMap( (val) => {

} )return Rx.DOM.getJSON( ‘data’ + val +‘.json' )

Flatten all observables to a meta stream

Page 64: Angular2 + rxjs

flatMapin conclusion

Is for scenarios when you come from one type of streams and wants it to become something completely else like a stream of clicks becomes a stream of ajax calls

clicks ajax persons

Also avoid thisajax

clicks

ajax

ajax

Subscribe

Subscribe

Subscribe

Instead becomeajax

clicks

ajax

ajax

ajax personsflat mapmap

one subscribe

Page 65: Angular2 + rxjs

Delay,the whole sequence

stream.delay(1000).subscribe( (val) => { var newTime = new Date().getTime(); console.log('Delayed', val + " " + (newTime - time));})

Rx.Observable.merge( Rx.Observable.of('Marco').delay(1000), Rx.Observable.of('Polo').delay(2000)).subscribe((val) => { var newTime = new Date().getTime(); console.log('Merged Delay', val + " " + (newTime - time));})

Delay….. 1 second

123

….. 1 second Marco

….. 2 secondPolo

Page 66: Angular2 + rxjs

Subjectthe double nature

Observer Observable

Subject

the subject can act as a proxy for a group of subscribers and a source

Page 67: Angular2 + rxjs

Subject

var subject = new Rx.Subject();

subject.subscribe((val) => { console.log( 'Produced by subject', val );});

subject.onNext(1);subject.onCompleted();

Acts like an observer

Acts like an observable

Page 68: Angular2 + rxjs

Subjectvar subject = new Rx.Subject();

var source = Rx.Observable.interval(500);

source.subscribe(subject);

subject.subscribe( (val) => { console.log('Sub', val); }, (err) => console.log(err), () => console.log('completed'));

setTimeout(function() { subject.onCompleted();}, 3000);

Pass subject as an observer

Receives all the values pushed out by the source

Able to stop receiving values

2

1

3

Page 69: Angular2 + rxjs

var subject = new Rx.Subject();

var source = Rx.Observable.interval(500).take(3);

source.subscribe( subject );

subject.subscribe((val) => { console.log('Subject', val);});

subject.onNext('Mess1');subject.onNext('Mess2');

setTimeout(function() { subject.onCompleted();}, 1600);

Listens to all values from source

Add to stream

Order important

SubjectonNext() before subscribe is lost

subject.onNext(1);

Page 70: Angular2 + rxjs

var subject = Rx.ReplaySubject();subject.onNext(1);

subject.subscribe((val) = > { console.log('Replay', val);})

subject.onNext(2);subject.onNext(3);

var subject = Rx.Subject();subject.onNext(1);

subject.subscribe((val) = > { console.log('Replay', val);})

subject.onNext(2);subject.onNext(3);

Normal subject, everything before subscribe is lost

Replay subject, nothing is lost

Subject - typesdont do this

1 2

Page 71: Angular2 + rxjs

BehaviourSubject

Good for default values

/* Initialize with initial value of 42 */var subject = new Rx.BehaviorSubject(42);

var subscription = subject.subscribe( function (x) { console.log('Next: ' + x.toString()); }, function (err) { console.log('Error: ' + err); }, function () { console.log('Completed'); });

subject.onNext(56);

subject.onCompleted();// => Completed

Init/Default value

Next: 42

Next: 56

Page 72: Angular2 + rxjs

Schedulersbending time

Page 73: Angular2 + rxjs

Because scheduler has its own virtual clockAnything scheduled on that scheduler will adhere to time denoted on the clock

I.e we can bend time for ex unit testing

Page 74: Angular2 + rxjs

var onNext = Rx.ReactiveTest.onNext;var scheduler = new Rx.TestScheduler();var subject = scheduler.createColdObservable( onNext(100,'first'), onNext(200,'second'));

var result;

subject.subscribe((val) => { result = val;});

scheduler.advanceBy( 100 );

console.log('Should equal', result === 'first');

scheduler.advanceBy( 100 );

console.log('Should equal', result === 'second');

Advance timeAssert

Advance timeAssert

create observable from scheduler

emit values

Page 75: Angular2 + rxjs

var testScheduler = new Rx.TestScheduler();

var stream = Rx.Observable.interval(1000, testScheduler).take(5).map((val) => { return val + 1}).filter((i) => { return i % 2 === 0});

var result;stream.subscribe((val) => result = val );

console.log('testing function’);

testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);

console.log('Should equal', result === 2);

testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);console.log('Should equal', result === 4);

replace default scheduler

0 1 2

Page 76: Angular2 + rxjs

Further reading

https://xgrommx.github.io/rx-book

http://www.learnrxjs.io/

bacon.js

Page 77: Angular2 + rxjs

Back to angular 2and some recipes

Page 78: Angular2 + rxjs

flatMap auto complete

flatmapExample = Rx.Observable.fromEvent(input,'keyup').map( function(ev){ return ev.target.value;}).filter(function(text){ return text.length >=3;}).distinctUntilChanged().flatMap( function(val){ return Rx.DOM.getJSON( 'data3.json' );})

flatmapExample.subscribe( function(result){ console.log('Flatmap', result);})

Transform event to char

Wait until we have 3 chars

Only perform search if this ‘search’ is unique

Page 79: Angular2 + rxjs

Debouncestop clicking the save button

var debounceTime = Rx.Observable.fromEvent(button,'click').debounce(2000);

debounceTime.subscribe( function(){ console.log('mouse pressed');})

Ignores all generated mouse click events

for 2 seconds

click click click click click

ignore ignore ignore ignore use

…. 2 seconds passed

Page 80: Angular2 + rxjs

Retryfor those shaky connections

var stream = Rx.DOM.get(‘/products.json’).delay( 5000 ).retry(5);

stream.subscribe((val) => { console.log('Data', val);}, err => console.log(err));

5 failed attempts then we hit error callback

Its so easy to retry, imagine how messy this code would be with a promise

Page 81: Angular2 + rxjs

Component to ComponentYou want something that can produce value,

that you can listen to

this.subject = new Rx.Subject();

function sendData(data){ this.subject.next( data )}

function getSubject(){ return this.subject;}

//service implComponent

1

Component2

service.getSubject().subscribe(()=>{})

service.sendData( data )

BUT, can be made nicer with a uniform data flow

Page 82: Angular2 + rxjs

Routingsetup

NgModule({ imports : [ BrowserModule … other modules RouterModule ], declarations : [ AppComponent, … other components ], bootstrap : [ AppComponent ]})export class AppModule {}

import { RouterModule } from ‘@angular/router’

Router serviceRouter directivesand our configured routes

contains

Page 83: Angular2 + rxjs

Routingcomponent based

[ { path : ‘jedis’, component : JediListComponent, }, { path : ‘jedis/:id’, component : JediComponent, }, { path : ‘’, redirect : ‘jedis’, pathMatch: ‘full’ }, { path : ‘**’, component : PageNotFoundComponent, }

]

domain/jedis

domain/jedis/1

domain/anything

Order matters, first match wins

Page 84: Angular2 + rxjs

Routingsetting up configured routes

NgModule({ imports : [ BrowserModule … other modules RouterModule.forRoot([ [{ path: ‘jedis’, component : ‘JediList’ }] .. and so on ]) ], declarations : [ AppComponent, … other components ], bootstrap : [ AppComponent ]})export class AppModule {}

Page 85: Angular2 + rxjs

Routingdirectives

<a [routerLink]=“[ ‘/jedis’ ]” >Jedis</a>

{ path: ‘jedis’, component : ‘JediList’}

<router-outlet></router-outlet>Where to render content, think ng-view

Creates a link using routerLink attribute directive

Corresponding configured route

Page 86: Angular2 + rxjs

Routing with parameters

{ path: ‘jedis/:id’, component : ‘JediDetail’}

<a [routerLink]=“[ ‘/jedis’, jedi.id ]” >{{ jedi.name }}</a><div *ngFor=“let jedi of jedis”>

</div>import { ActivatedRoute } from ‘@angular/route’;

export class JediDetail{ constructor(private route:ActivatedRoute){ this.route.snapshot.params[‘id’]; // get data by id }}

Page 87: Angular2 + rxjs

Routingrouting service

import { Router } from ‘@angular/router’export class JediDetailComponent{ constructor( router:Router ) { } onSave(){ this.service .save( someData ) .subscribe((result) => { this.router.navigate([‘/jedis’]); }) }}

this.router.navigate([‘url’,<parameter>]);

Page 88: Angular2 + rxjs

Routing guardsCanActivate

CanDeactivate

Resolve

CanLoad

Is it allowed to navigate to this route

Is it allowed to navigate away from this route

Prefetch data before going to the route

Prevent async routing

Page 89: Angular2 + rxjs

CanActivateexample

export class AuthenticateGuard implements CanActivate { constructor( private user:User ){

}

canActivate():boolean { return this.user.roles.contains(‘Administrator’); } }

RouterModule.forRoot([ { path: ’adminpage’, canActivate : [ AuthenticateGuard ], component : AdminPageComponent }])

Page 90: Angular2 + rxjs

angular-cli

forms

augury

Further reading

angular-universal

AOT compilation

scaffolding tool

debug tool for browser

very competent way of dealing with forms and validation

serverside rendering

precompilation of code, faster, smaller bundle, no JIT, no compiler in bundle

Page 91: Angular2 + rxjs

Thank you