angulartypescriptrxjsobservableasync-pipe

Async pipe not updating the view


Repro

https://stackblitz.com/edit/angular-rlqkyb

app.service

Simple service creating a BehaviorSubject with some default values, a function to return this subject as an observable and finally a function to emit a new value on the subject.

app.component

This component simply gets a reference to the observable returned by the service and subscribe to it via the async pipe. Note that the observable is piped in order to display the emitted value.

This component also displays the second component.

app2.component

This component has an input that subscribes to the observable returned by the service that emits a new value and that's it.

The problem

The problem is that even though I see the new value emitted in the console, the view of app.component is not updated.

At first, I thought that it was because the observable returned by the service was not in the zone, so I tried wrapping some part of this function into a zone.run (the whole content of the observable and just the next/complete calls) but it still didn't work.

One thing that works is the commented line in app2.component that subscribes to the observable using a setTimeout.

Another thing that works is to set the change detection strategy to default instead of OnPush, however, I do not wish to do so.

In my mind, the app.component gets the observable, subscribes to it via the async pipe (so it should refresh the value as soon as it receives a new value, that's the goal of the observable, indicating to Angular that something has changed). At the same time, app2.component gets loaded and via its input, it updates the observable, so the async pipe of app.component should get the new value and update its view as it is doing without the OnPush strategy.

I'm pretty confident it's not an angular issue but rather a misunderstanding from me, but then I'd appreciate if someone could explain me where I'm wrong :-)

Note that I also checked the other thread on StackOverflow but they all talk about zone.run or using detectChanges (which I wouldn't know where to use it here)


Solution

  • The emission of a new value from the observable marks the OnPush component to be checked in the next change detection cycle. But I think in your case, there is simply nothing happening that triggers a CD cycle in the application. That explains why the line with the setTimeout fixes it: setTimeout triggers a CD cycle and because the component is marked to be checked, it is updated.