#async pipe being weird

6 messages · Page 1 of 1 (latest)

rose pelican
#

Hi, this is something I already fixed but still don't understand why it happens, I hope someone can guide me:

  1. on a component I have the following observable:
  protected combinedObservable$ = combineLatest([
    this.observable1$, //this is basically a state that gets filled with an http request
    this.myForm.get('myfield').valueChanges //a listener to a formControl change
  ]).pipe(
    map(([httpResponse, controlValue]) =>{
      const result = httpResponse?.filter(r => controValue.includes(r.id));
      console.log({httpResponse, controlValue, result});
      return result; //result is an array
    }),
  );

so then I go to the component's template:

<div *ngIf="(combinedObservable$ | async)?.length > 0">
  obsValue: {{combinedObservable$ | async}} //this prints null
  obsLength: {{(combinedObservable$ | async)?.length}} // this prints 0
  condition: {{(combinedObservable$ | async)?.length > 0}} //this prints false
</div>

the issue is, when I first change the value of myfield and the map operation is triggered, on console I see that the result is an array with 1 element, but then on my page I see that the div with the ngIf and its content gets rendered but the content of the div has outdated data from the subscription (value is null, length is 0 and the exact same condition from the ngIf is false), and this keeps happening until I select a new value.

I read something about cold and hot observables and then I fixed it adding a shareReplay(1) to my combinedObservable$. But I still dont understand why the async pipe behaves different when rendering changes and when inside an ngIf

#

ok right after posting this I tried putting obsValue: {{combinedObservable$ | async}} at the beggining of the template and then it gets prepopulated but then the ngIf doenst update to true and the other content is not shown.

seeing it gets fixed with the shareReplay I guess this happend because each | async is a new subscription to the initial observable? I don't know, in my ignorant mind this seems wacky, but I can't think of another way that it could work

opal gust
#

A minimal stackblitz reproduction would be easier to grasp.
Anyway, I'd start to avoid multiple subscription:

<div *ngIf="(combinedObservable$ | async as combObs)?.length > 0">
  obsValue: {{combObs}}
  obsLength: {{combObs?.length}}
  condition: {{combObs?.length > 0}}
</div>
rose pelican
#

your code throws
Parser Error: Missing expected ) at column 30 in [(combinedObservable$ | async as combObs)?.length > 0] in

abstract grove
opal gust
#

Yes, my bad. I forgot you cannot directly access the exported variable.
Just adding that you can avoid to render the further div with a ng-container.

<ng-container *ngIf="combinedObservable$ | async as combObs">
  <div *ngIf="combObs.length > 0">
    obsValue: {{combObs}}
    obsLength: {{combObs?.length}}
    condition: {{combObs?.length > 0}}
  </div>
</ng-container>