简体   繁体   English

使用 OnPush 策略进行变更检测的意外行为

[英]Unexpected behaviour of change detection with OnPush strategy

I've faced with very strange behaviour of change detector (with OnPush).我遇到了非常奇怪的变化检测器行为(使用 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尽管如此,我还是在stackblitz上的stackblitz非常简单的例子中重现了这种行为

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

There are three components: main app component, data component and loading indicator component.共有三个组件:主app组件、 data组件和loading indicator组件。 Main component can display the data component through the <router-outlet> when user click the Go to data link.当用户单击Go to data链接时,主组件可以通过<router-outlet>显示data组件。 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.当用户点击Go to datadata组件请求该服务获取数据,加载有一些延迟。 To emulate this delay I use rxjs timer, but with HttpClient the behaviour is absolutely the same.为了模拟这种延迟,我使用了 rxjs 计时器,但使用HttpClient的行为完全相同。 Data & loading values are binded by the use of async pipe.数据和加载值通过使用async管道绑定。 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).但是由于某种原因数据按预期显示,但loading不是(最好说它不时起作用,但并非总是如此)。 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.我还将值记录到控制台,在那里很容易看到loading$ observable 发出值,但@Input绑定由于某种原因不起作用。 Why it works so, and how to fix that?为什么它如此有效,以及如何解决这个问题?

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.它不起作用的原因是因为您在检查父组件后从子组件发出this._loading.next(true)值。

Async pipe does triggers subscription and works as intended , that is runs cdRef.markForCheck();异步管道确实会触发订阅并按预期工作,即运行cdRef.markForCheck(); . . But it doesn't run change detection but rather only marks component, where AsyncPipe is used, to be checked .但它不运行更改检测,而只是将使用 AsyncPipe 的组件标记为要检查 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要修复它,您可以将this._common.getData()包装在您的子组件中的微任务中

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

Forked Stackblitz 分叉的堆栈闪电战

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM