44

Angular Component Subscription vs. AsyncPipe: Use Pipes When Possible

 5 years ago
source link: https://www.tuicool.com/articles/hit/i6vqimb
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

I typically review a fair amount of Angular code at work. One thing I typically encourage is using plain Observable s in an Angular Component, and using AsyncPipe ( foo | async ) from the template html to handle subscription, rather than directly subscribing to an observable in a component TS file.

Subscribing in Components

Unless you know a subscription you’re starting in a component is very finite (e.g. an HTTP request with no retry logic, etc), subscriptions you make in a Component must:

ngOnInit

Consider:

@Component()
 export class Foo
   implements OnInit, OnDestroy {

   someStringToDisplay = '';
   private readonly onDestroy =
     new ReplaySubject<void>(1);

   ngOnInit() {
     someObservable.pipe(
       takeUntil(this.onDestroy),
       map( ... ),
     ).subscribe(next => {
       this.someStringToDisplay = next;
       this.ref.markForCheck();
     });
   }

   ngOnDestroy() {
     this.onDestroy.next(undefined);
   }
 }
@Component()
 export class Foo
   implements OnInit, OnDestroy {

   someStringToDisplay = '';
   private subscription =
     Subscription.EMPTY;

   ngOnInit() {
     this.subscription = someObservable.pipe(
       map( ... ),
     ).subscribe(next => {
       this.someStringToDisplay = next;
       this.ref.markForCheck();
     });
   }

   ngOnDestroy() {
     this.subscription.unsubscribe();
   }
 }
<span>{{someStringToDisplay}}</span>

AsyncPipe can take care of that for you

@Component() export class Foo {
   someStringToDisplay = someObservable.pipe(
     map(...),
   );
 }
<span>{{someStringToDisplay | async}}</span>

Much better! No need to remember to manage unsubscribe. No need to implement OnDestroyAsyncPipe does its own unsubscribe on destruction, etc. If you only implement  OnInit to make a new subscription, you can forego that too.

Best Practice: Use publishReplay and refCount if accessing the same Observable from multiple places

If you need to access a value multiple times, consider using the publishReplay and refCount RxJS operators:

readonly pageTitle = this.route.params.pipe(
   map(params => params['id']),
   flatMap(id => this.http.get(
     `api/pages/${id}/title`,
     {'responseType': 'text'})),
   publishReplay(1),
   refCount()
);
<h1>{{pageTitle | async}}</h1> 
<p>You are viewing {{pageTitle | async}}.</p>

This will cause the template rendering to make a single request for pageTitle , and cache the result between both uses.

Best Practice: Combine *ngIf , as , and else with AsyncPipe

If you need to handle the loading state, and need to display nested properties of an object returned from an observable, you can do something like:

<ng-container *ngIf="(pageObservable | async) as page; else loading">
   <!-- can refer to 'page' here -->
   <h1>{{page.title}}</h1>
   <p>{{page.paragraph}}</p>
 </ng-container>
 <ng-template #loading>Loading…</ng-template>

Note that this doesn’t distinguish between the case where pageObservable is still loading, and the case where pageObservable resolved to a falsey value.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK