Slide 1

Slide 1 text

Reactive Programming with RxJS Shmuela Jacobs

Slide 2

Slide 2 text

Shmuela Jacobs ng-girls.org Front-end Consultant & Trainer

Slide 3

Slide 3 text

Reactive

Slide 4

Slide 4 text

Functional & Reactive Programming • TIM: https://youtu.be/hg5GzC_nXDo?t=8s • Modern Times: https://youtu.be/DfGs2Y5WJ14?t=9s

Slide 5

Slide 5 text

Using RxJS - everything is an event stream • Http • Websockets • DOM events • Animations • And more...

Slide 6

Slide 6 text

Angular and RxJS • Routing • Http (HttpClient) • AngularFire2 • Realtime DB - lists and objects • DB queries • User status • ngRx

Slide 7

Slide 7 text

Observer Pattern

Slide 8

Slide 8 text

Observer Pattern Observable Subject Subscriber Subscription

Slide 9

Slide 9 text

Subscription @Component({ ... template: `

{{ user?.name }}

` }) export class RecipeComponent implements OnInit, OnDestroy { user; subscription; constructor(private userService: UserService) {} ngOnInit() { this.subscription = this.userService.user$.subscribe(user => { this.user = user; }); ngOnDestroy() { this.subscription.unsubscribe(); }

Slide 10

Slide 10 text

Async Pipe @Component({ ... template: `

{{ (user$ | async)?.name }}

{{ recipe?.id }}: {{ recipe?.title }}

` }) export class RecipeComponent implements OnInit { @Input() recipeId; recipe$; user$; constructor(private recipeService: RecipeService) {} ngOnInit() { this.user$ = this.userService.getUser(); this.recipe$ = this.recipeService.getRecipe(this.recipeId); }

Slide 11

Slide 11 text

Manipulate Data - map @Injectable() export class DataService { constructor(private http: httpClient) {} getItem(id): Observable { return this.http.get(`https:/data.com/items/${id}`) .map(res => { return { ...res, id }; }) } }

Slide 12

Slide 12 text

Useful (Popular) Operators import 'rxjs/add/operator/filter'; numbers$ .filter(res => res > 100) .scan((total, current) => total + current) .do(res => updateNewTotal(res));

Slide 13

Slide 13 text

Get User Data - switchMap @Injectable() export class UserService { user$; constructor( afAuth: AngularFireAuth, afDB: AngularFireDatabase) { this.user$ = afAuth.authState.switchMap(authDetails => {
 if (authDetails) { return afDB.object(`users/${authDetails.uid}`) } else { return Observable.of(null); } }); } }

Slide 14

Slide 14 text

Routing export declare class ActivatedRoute { /** An observable of the URL segments matched by this route */ url: Observable; /** An observable of the matrix parameters scoped to this route */ params: Observable; /** An observable of the query parameters shared by all the routes */ queryParams: Observable; /** An observable of the URL fragment shared by all the routes */ fragment: Observable; /** An observable of the static and resolved data of this route. */ data: Observable; ... }

Slide 15

Slide 15 text

Routing Params - forEach @Component({ selector: 'app-recipe-display', template: ` ` }) export class RecipeComponent implements OnInit { recipe$; constructor( private recipeService: RecipeService, private route: ActivatedRoute) { } ngOnInit() { this.route.params.forEach(param => { this.recipe$ = this.recipeService.getRecipe(param.id);
 }); }

Slide 16

Slide 16 text

Route Guards @Injectable() export class AuthGuard implements CanActivate { constructor(private userService: UserService) {} canActivate() { return this.userService.userData$.map(user => { return user.isAdmin; }); } } const routes: Routes = [ { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }, ... ];

Slide 17

Slide 17 text

Route Guards CanActivate to mediate navigation to a route.
 CanActivateChild to mediate navigation to a child route.
 CanDeactivate to mediate navigation away from the current route.
 Resolve to perform route data retrieval before route activation.
 CanLoad to mediate navigation to a feature module loaded asynchronously.


Slide 18

Slide 18 text

Dialog - Behavior Subject, next @Injectable() export class DialogService { dialog$ = new BehaviorSubject(null); createDialog(data) { const dialogData = { component: this.getDialogComponent(data), instanceData: { ... } // based on data }; this.dialog$.next(dialogComponentData); }

Slide 19

Slide 19 text

Dialog - Behavior Subject, next ngOnInit() { this.dialogSubscription = this.dialog$.subscribe((dialogComponentData) => { this.mdDialog.open(dialogComponentData.component, { viewContainerRef: this.viewContainer, data: dialogComponentData.instanceData ... }); } } ngOnDestroy() { this.dialogSubscription.unsubscribe(); }

Slide 20

Slide 20 text

Creating Observables const observable$ = Rx.Observable.create( observer => { observer.next( 'hello' ) observer.next( 'world' ) }) const clicks$ = Rx.Observable.fromEvent(document, 'click') const promise$ = Rx.Observable.fromPromise(promise) const timer$ = Rx.Observable.timer(1000) const interval$ = Rx.Observable.interval(1000) const someArray = [...] const things$ = Rx.Observable.of(...someArray) // Array, an array-like object, a Promise, // an iterable object, or an Observable-like object const anything = Rx.Observable.from(someArray)

Slide 21

Slide 21 text

DOM Events - fromEvent constructor(private el: ElementRef) {} ngOnInit(){ const nativeEl = this.el.nativeElement; const document = nativeEl.getRootNode(); const mousedown$ = Observable.fromEvent(nativeEl, 'mousedown')
 const mousemove$ = Observable.fromEvent(document, 'mousemove')
 const mouseup$ = Observable.fromEvent(document, 'mouseup'); const mousedrag$ = mousedown$.mergeMap((md: MouseEvent) => { return mousemove$.map((mm: MouseEvent) => { return { ... }; // new position }).takeUntil(mouseup$); }); this.subscription = mousedrag$.subscribe((pos) => { ... // update position }); }

Slide 22

Slide 22 text

Completing Observables takeUntil(observable$): emits values until observable$ emits a value. take(n): emits N values before stopping the observable. takeWhile(predicate): tests the emitted values against a predicate, if it returns `false`, it will complete. first(): emits the first value and completes. first(predicate): checks each value against a predicate function, if it returns `true`, the emits that value and completes.

Slide 23

Slide 23 text

More in Observables • Hot vs. Cold • Catch, Retry • Handle stream overload: • throttleTime • debounceTime • bufferTime, bufferCount

Slide 24

Slide 24 text

Resources • RxJS Docs: http://reactivex.io/rxjs/ • Ben Lesh (YouTube, blog posts, conferences) • Interactive diagrams of Rx Observables: http://rxmarbles.com/ • 20 Examples: https://angularfirebase.com/lessons/rxjs-quickstart-with-20- examples/ • Thoughtram blog - three things you didn't know about the async pipe: 
 https://blog.thoughtram.io/angular/2017/02/27/three-things-you-didnt-know- about-the-async-pipe.html • Victor Savkin - Testing Race Conditions Using RxJS Marbles:
 https://blog.nrwl.io/rxjs-advanced-techniques-testing-race-conditions-using- rxjs-marbles-53e7e789fba5

Slide 25

Slide 25 text

Shmuela Jacobs [email protected] shmool @ShmuelaJ 6JCPM;QW ng-girls.org www.meetup.com/Angular-Nights