Angular Component Subscription vs. AsyncPipe: Use Pipes When Possible
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 OnDestroy
. AsyncPipe
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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK