Reputation: 2445
I have an observable that when I use the rxjs 'share' operator behaves strangely in the Angular template. Here's how I setup my two observables (in a service):
this.family = this.store.select(getFamily).pipe(
tap(family => {
if (family == null) {
this.loadFamily();
}
}),
filter(family => family != null),
share(),
);
this.familyMembers = this.family.pipe(
map(family => {
return family.familyMembers;
})
);
I then add the familyMember observable to my component as a property
this.familyMembers = this.familyService.familyMembers
My markup looks something like this:
<tbody *ngIf="(familyMembers | async) != null">
<pre> {{(familyMembers | async) | json}}</pre>
Now the strange thing is when familyMembers is populated (an array of two), the <pre>
will render with null
inside even after i've checked for null with the *ngIf
.
I also have a list that does not render anything as follows:
<ng-template ngFor let-familyMember let-i="index" [ngForOf]="(familyMembers | async)" [ngForTrackBy]="trackByFn">
I know that the last thing familyMembers
observable emitted was the correct two family members, I've checked this with map
and console.log
. The Angular template seems to be seeing this as null.. but also not?
Everything works if I remove the share()
from the family observable but the tap()
runs twice which is not desirable.
EDIT:
Using shareReplay() instead of share() fixed my problem. share() will not re-emit values when all subscriptions unsubscribe.
Upvotes: 1
Views: 274
Reputation: 2290
You should use the shared() operator since you are subscribe twice, that's why you got the duplicate.
You should change your html code accordingly :
<tbody *ngIf="familyMembers | async as familyProps">
<pre> {{familyProps | json}}</pre>
In the first *ngIf you check for the existence of the values, so there is no need to use async pipe again in the other row since it won't be called if its not passed the first one.
Plus, when you calling 'as familyProps' and using it in the next line, you are sure that this local object will be available when this line executes. It might work without the 'as', but from your comment, I just add it to make sure.
--EDIT
Each async pipe that you call will subscribe too, now what happen is that the first line with *ngIf waiting for the values, and the second line located inside this wrapper, so it won't call until the first line executes.
Now, when the first line pass, the second line will try to subscribe with the async pipe but its already completes, because of the first line subscription and the shared() operator.
You can try this, and it should work as well:
<tbody *ngIf="familyMembers | async">
<pre> {{familyMembers | json}} </pre>
Upvotes: 1