Reputation: 571
When using the AsyncPipe
inside of an *ngIf
, if the Observable
connected to the AsyncPipe
pushes its values before the *ngIf
becomes true, the value returned from the AsyncPipe
will be incorrect.
For example, let's say I have:
<div *ngIf="showPipe">
<div *ngFor="let item of arrObs | async">{{item}}</div>
</div>
Then say events happen in this order:
showPipe
is falsearrObs
pushes [1,2,3]showPipe
is set to trueFrom what I've seen, the *ngFor
will act as if arrObs | async
returned null.
One solution to this problem is to use [hidden]
instead, but there are a lot of benefits to *ngIf
, like performance and making null handling easier.
What's the proper way to do this? Should I just not use an observable at all for displaying content? I had assumed that using an observable was the most Angular-y way of doing things.
Edit:
My observable is actually just a new Subject()
which I call next()
on.
Upvotes: 3
Views: 2982
Reputation: 2932
In NgRx 10 and above, you can use the *ngrxLet
directive along with the @ngrx/component
package (documentation).
Usage example:
<ng-container *ngrxLet="observableNumber$ as n">
<app-number [number]="n">
</app-number>
</ng-container>
Upvotes: 0
Reputation: 71891
There are some ways you can solve this. I would suggest adding a shareReplay
operator on your Observable
:
readonly arrObs = this.someDataFromSomewhere().pipe(
shareReplay(1)
);
if your Observable
is actually a Subject
, you can also change it to a BehaviorSubject
or ReplaySubject
This way you will always receive the most up to date data on subscription.
There are also ways to handle this in the template and still maintain the *ngIf
benefits:
<ng-container *ngIf="arrObs | async as arr">
<div *ngIf="showPipe">
<div *ngFor="let item of arr">{{item}}</div>
</div>
</ng-container>
Or if you don't want the *ngIf
to wait for the observable
<ng-container *ngIf="{ arr: arrObs | async } as data">
<div *ngIf="showPipe">
<div *ngFor="let item of data.arr">{{item}}</div>
</div>
</ng-container>
Upvotes: 4
Reputation: 156
Surround your structure in an ng-container
which contains the async pipe and stores the value in a variable using as
. Also use the ng-if
directive to load the container only after you get a value in that async pipe.
<ng-container *ngIf="( arrObs| async ) as array">
<div *ngIf="showPipe">
<div *ngFor="let item of array">{{item}}</div>
</div>
</ng-container>
Upvotes: 2