observables structural directives nils mehlhorn
play

OBSERVABLES + STRUCTURAL DIRECTIVES = NILS MEHLHORN - PowerPoint PPT Presentation

OBSERVABLES + STRUCTURAL DIRECTIVES = NILS MEHLHORN nils-mehlhorn.de www freelance software engineer @n_mehlhorn founder of scenelab.io 2 NGRX BOOK Pay what you want for the complete learning resource gum.co/angular-ngrx-book


  1. OBSERVABLES + STRUCTURAL DIRECTIVES = ♥

  2. NILS MEHLHORN nils-mehlhorn.de www freelance software engineer @n_mehlhorn founder of scenelab.io 2

  3. NGRX BOOK Pay what you want for the complete learning resource gum.co/angular-ngrx-book @n_mehlhorn 3

  4. @Component({...}) export class UsersComponent implements OnInit { users: User[] = [] constructor(private userService: UserService) {} ngOnInit() { this.userService.getAll().subscribe(users => { this.users = users }) } } <p>{{ users.length }} users online</p> You forgot to unsubscribe! 🙆 … do you have to unsubscribe everytime? 🤕 @n_mehlhorn 4

  5. WHY UNSUBSCRIBE? One-Off Observables e.g. HTTP request, timer cancellation ➝ “observable etiquette” ➝ @n_mehlhorn 5

  6. CANCELLATION server q e r P T T H clicks app “submit” user @n_mehlhorn 6

  7. CANCELLATION cancelled if not yet done server q e r P T T H navigates app away user @n_mehlhorn 7

  8. WHY UNSUBSCRIBE? One-Off Observables Long-Lived Observables e.g. HTTP request, timer e.g. store, router events cancellation no memory leak ➝ ➝ “observable etiquette” ➝ @n_mehlhorn 8

  9. app MEMORY LEAK component service subscribe() observable cut by unsubscribe or completion subscriber Recommended Read How to create a memory leak in Angular -- Kevin Kreuzer @n_mehlhorn 9

  10. Observable is just a function that takes an observer and returns a function Ben Lesh RxJS Lead @n_mehlhorn 10

  11. subscribe() callbacks or Subject passed to subscribe() Observable is just a function that takes an observer and returns a function Ben Lesh cancellation returned by subscribe() RxJS Lead @n_mehlhorn 11

  12. @Component({...}) export class UsersComponent implements OnInit, OnDestroy { users: User[] subscription: Subscription constructor(private userService: UserService) {} ngOnInit() { this.subscription = this.userService.getAll().subscribe(users => { this.users = users }) } ngOnDestroy() { this.subscription.unsubscribe() } } IMPERATIVE MANUAL SUBSCRIPTION MANAGEMENT 12 @n_mehlhorn

  13. @Component({...}) export class UsersComponent implements OnInit, OnDestroy { users: User[] destroy$ = new Subject<void>() constructor(private userService: UserService) {} ngOnInit() { this.userService.getAll() .pipe(takeUntil(this.destroy$) .subscribe(users => { this.users = users }) } ngOnDestroy() { this.destroy$.next() } } DECLARATIVE MANUAL SUBSCRIPTION MANAGEMENT @n_mehlhorn 13

  14. rxjs-tslint-rules MANUAL SUBSCRIPTION MANAGEMENT verbose & error-prone 👏 full control 👎 OnPush change detection 👏 access to values from 👎 requires trigger other methods falsy values 👎 required for observables not reflected in view (e.g. updating a user) Recommended Read Loading Indication in Angular -- Nils Mehlhorn @n_mehlhorn 14

  15. @Component({ ... changeDetection: ChangeDetectionStrategy.OnPush }) export class UsersComponent implements OnInit { users$: Observable<User[]> constructor(private userService: UserService) {} ngOnInit() { this.users$ = this.userService.getAll() } } <p *ngIf="users$ | async as users; else loading"> {{ users.length }} users online </p> <ng-template #loading>Loading…</ng-template> AUTOMATIC SUBSCRIPTION MANAGEMENT WITH NGIF & ASYNCPIPE @n_mehlhorn 15

  16. @Component({ ... changeDetection: ChangeDetectionStrategy.OnPush }) export class UsersComponent implements OnInit { users$: Observable<User[]> constructor(private userService: UserService) {} ngOnInit() { this.users$ = this.userService.getAll() } } <p *ngIf="users$ | async as users; else loading"> {{ users.length }} users online </p> <ng-template #loading>Loading…</ng-template> AUTOMATIC SUBSCRIPTION MANAGEMENT WITH NGIF & ASYNCPIPE @n_mehlhorn 16

  17. ASYNCPIPE @Pipe({name: 'async', pure: false}) export class SimpleAsyncPipe implements OnDestroy, PipeTransform { private latestValue: any = null private subscription: Subscription = null unsubscribes ● constructor(private cd: ChangeDetectorRef) {} triggers change ● transform(observable: Observable<any>): any { detection this.subscription = observable.subscribe(value => { this.latestValue = value this.cd.markForCheck() }) return WrappedValue.wrap(this.latestValue) } ngOnDestroy(): void { this.subscription.unsubscribe() } } @n_mehlhorn 17

  18. @Component({ ... changeDetection: ChangeDetectionStrategy.OnPush }) export class UsersComponent implements OnInit { users$: Observable<User[]> constructor(private userService: UserService) {} ngOnInit() { this.users$ = this.userService.getAll() } } <p *ngIf="users$ | async as users; else loading"> {{ users.length }} users online </p> <ng-template #loading>Loading…</ng-template> AUTOMATIC SUBSCRIPTION MANAGEMENT WITH NGIF & ASYNCPIPE @n_mehlhorn 18

  19. ONPUSH CHANGE DETECTION updates view only when 1. @Inputs are reassigned 2. events occur on component or children 3. markForCheck() called ➜ faster due to less updates source: angular/change_detection_spec.ts L282 @n_mehlhorn 19

  20. @Component({ ... changeDetection: ChangeDetectionStrategy.OnPush }) export class UsersComponent implements OnInit { users$: Observable<User[]> constructor(private userService: UserService) {} ngOnInit() { this.users$ = this.userService.getAll() } } <p *ngIf="users$ | async as users; else loading"> {{ users.length }} users online </p> <ng-template #loading>Loading…</ng-template> AUTOMATIC SUBSCRIPTION MANAGEMENT WITH NGIF & ASYNCPIPE @n_mehlhorn 20

  21. Structural directives are responsible for HTML layout. They shape or reshape the DOM's structure, typically by adding, removing, or manipulating elements. Angular Docs @n_mehlhorn 21

  22. STRUCTURAL DIRECTIVES: MICROSYNTAX <p *ngIf="users$ | async as users; else loading"> {{ users.length }} users online </p> <ng-template #loading>Loading…</ng-template> microsyntax desugaring <ng-template [ngIf]="users$ | async as users" [ngIfElse]="loading"> <p>{{ users.length }} users online</p> </ng-template> <ng-template #loading>Loading…</ng-template> @n_mehlhorn 22

  23. STRUCTURAL @Directive({selector: '[ngIf]'}) export class SimpleNgIf<T> { DIRECTIVES: NGIF elseTemplate: TemplateRef context: NgIfContext<T> = {} <ng-template constructor(private view: ViewContainerRef, [ngIf]=”users$ | async as users” private template: TemplateRef<NgIfContext<T>>) {} [ngIfElse]=”loading”> @Input() <p>{{ users.length }} online</p> set ngIfElse(template: TemplateRef) { </ng-template> this.elseTemplate = template <ng-template #loading> } Loading… </ng-template> @Input() set ngIf(condition: T) { this.context.$implicit = this.context.ngIf = condition this.view.clear() interface NgIfContext<T> { if (condition) { $implicit: T this.view.createEmbeddedView(this.template, this.context) } else { ngIf: T this.view.createEmbeddedView(this.elseTemplate) } } } } @n_mehlhorn 23

  24. explicit binding to explicit binding to ngIf-property $implicit-property POP QUIZ: NGIF What’s the output? 1. hello, ngIf, undefined, hello <ng-template [ngIf]="'hello'" let-a="$implicit" let-b="ngIf" let-c> 2. undefined, undefined, hello, hello <p>{{ a }}</p> 3. hello, hello, hello, hello <p>{{ b }}</p> <p>{{ c }}</p> 4. hello, undefined, hello, hello </ng-template> <p *ngIf="'hello' as d">{{ d }}</p> interface NgIfContext<T> { $implicit: T ngIf: T } implicit binding to implicit binding to ngIf-property $implicit-property this.context.$implicit = this.context.ngIf = condition // 'hello' @n_mehlhorn 24

  25. NGIF & ASYNCPIPE no falsy values 👏 succinct 👎 no access to errors 👏 OnPush change 👎 detection same template for 👏 loading and error states fallback template 👎 (no access to values from 👏 other methods) @n_mehlhorn 25

  26. *observe A Structural Directive for Observables @n_mehlhorn 26

  27. <p *observe="users$ as users; before loadingTemplate; error errorTemplate"> {{ users.length }} users online </p> <ng-template #loadingTemplate> <p>Loading ...</p> </ng-template> <ng-template #errorTemplate let-error> <p>{{ error }}</p> </ng-template> DEMO AUTOMATIC SUBSCRIPTION MANAGEMENT WITH OBSERVE @n_mehlhorn 27

  28. <p *observe="users$ as users; @Directive({ before loadingTemplate; selector: "[observe]" error errorTemplate"> }) {{ users.length }} users online export class ObserveDirective<T> implements OnDestroy,OnInit { </p> <ng-template #loadingTemplate> constructor( <p>Loading ...</p> </ng-template> private view: ViewContainerRef, <ng-template #errorTemplate let-error> private nextRef: TemplateRef<ObserveContext<T>>, <p>{{ error }}</p> private changes: ChangeDetectorRef </ng-template> ) {} ... } @n_mehlhorn 28

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend