Search code examples
angularangular-changedetection

Unexpected behaviour of change detection with OnPush strategy


I've faced with very strange behaviour of change detector (with OnPush). I am not even able to explain it to myself and don't know how to fix it. Nevertheless I've reproduced this behaviour in very simple example on stackblitz

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

There are three components: main app component, data component and loading indicator component. Main component can display the data component through the <router-outlet> when user click the Go to data link. There is also service (aka state of the application). When user click Go to data, the data component requests this service for data, which is loaded with some delay. To emulate this delay I use rxjs timer, but with HttpClient the behaviour is absolutely the same. Data & loading values are binded by the use of async pipe. But for some reason data is displayed as expected, but loading isn't (it's better to say that it works from time to time, but not always). That causes the loading indicatior doesn't appear on the screen. I also log values to the console where it's easy to see that the loading$ observable emits value, but the @Input binding doesn't work for some reason. Why it works so, and how to fix that?


Solution

  • The reason why it is not working is because you're emitting this._loading.next(true) value from child component after parent component has been checked.

    Async pipe does triggers subscription and works as intended, that is runs cdRef.markForCheck();. But it doesn't run change detection but rather only marks component, where AsyncPipe is used, to be checked. Once a new change detection cycle starts your component will recognize input changes.

    To fix it you can wrap this._common.getData() in microtask in your child component

    Promise.resolve().then(() => {
       this._common.getData();
    });
    

    Forked Stackblitz