问题
Given a template that looks like something like this
<some-component
*ngIf="someColdObservable$ | async"
[result]="someColdObservable$ | async"
></some-component>
and an observable that looks like this:
someColdObservable$: this.store.pipe(
select(isAllowedToDoThis),
filter(Boolean),
flatMap(() => apiRequest())
);
The someColdObservable$
gets subscribed to twice (as expected), which in turn issues two api calls (this is obviously a code smell, but let's disregard that at the moment).
In this scenario some-component
does not contain any null checks, and AsyncPipe
will return null if apiRequest
has not emitted a value before the AsyncPipe
is evaluated in the template resulting in some-component
throwing a cannot access x of null
(as [result]
is still null at this point) see AsyncPipe source for reference.
This is all expected behaviour (or at least after reading the source) however, when I try to mitigate the issue with making two requests by adding the shareReplay
to someColdObservable$
I also fix the issue of [result]
being null before the apiRequest()
emits a value. This makes little sense to me as I would expect AsyncPipe
to still return a null
_latestValue here, leaving the cannot access x of null
error unfixed. But for some reason adding shareReplay
fixes both of the aforementioned issues.
This is similar to Angular observable behavior odd when shared however, there is still the unanswered question of why shareReplay
fixes the issue.
Would someone be able to point out what I am missing here and why AsyncPipe
no longer returns null
before apiRequest()
emits a value?
Appreciate any pointers and input, thanks!
回答1:
First scenario:
- the ngIf expression is evaluated. It subscribes to the observable and evaluates to null. So the [result] expression is not evaluated since the ngIf expression is falsy.
- the observable emits, and the ngIf expression becomes truthy. So the [result] expression is evaluated. Its own async pipe subscribes to the cold observable which sends another request, and evaluates to null, which is thus passed as input to the component.
Second scenario:
- the ngIf expression is evaluated. It subscribes to the observable and evaluates to null. So the [result] expression is not evaluated since the ngIf expression is falsy.
- the observable emits, and the ngIf expression becomes true. So the [result] expression is evaluated. Its own async pipe subscribes to the hot observable which immediately replays what is has last emitted, and doesn't send a request anymore. The expression is thus evaluated to a non-null value, which is passed as input to the component.
A better solution than using shareReplay would be to store the result of the async pipe in a variable and use it:
<some-component
*ngIf="someColdObservable$ | async as result"
[result]="result"
></some-component>
来源:https://stackoverflow.com/questions/59109448/asyncpipe-initial-value-null-only-if-subscription-is-not-shared