所以我想更好地了解Angulars ChangeDetection并偶然发现一个问题:https://plnkr.co/edit/M8d6FhmDhGWIvSWNVpPm?p=preview
这个Plunkr是我的应用程序代码的简化版本,基本上有一个 parent 和一个 child 组件 . 两者都启用 ChangeDetectionStrategy.OnPush
.
parent.component.ts
@Component({
selector: 'parent',
template: `
<button (click)="click()">Load data</button>
{{stats.dataSize > 0}}
<span *ngIf="stats.dataSize > 0">Works</span>
<child [data]="data" [stats]="stats" (stats)="handleStatsChange()"></child>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ParentComponent implements OnCheck, OnChanges {
data = [];
stats = {
dataSize: 0
};
constructor(private cdr: ChangeDetectorRef) {
}
click() {
console.log("parent: loading data");
setTimeout(() => {
this.data = ["Data1", "Data2"];
this.cdr.markForCheck();
});
}
handleStatsChange() {
console.log('parent: stats change');
this.cdr.markForCheck();
}
}
child.component.ts
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, SimpleChanges } from "@angular/core";
@Component({
selector: 'child',
template: `
<div *ngFor="let item of data">{{item}}</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit, OnChanges {
@Input() data;
@Input() stats;
@Output('stats') statsEmitter = new EventEmitter();
constructor() {
}
ngOnInit() {
}
ngOnChanges(changes: SimpleChanges): void {
console.log("child changes: ", changes);
this.stats.dataSize = changes['data'].currentValue.length;
this.statsEmitter.emit(this.stats);
}
}
因此,父按钮点击按钮时会更新 data
,这会触发子项中的 ngOnChanges
. 每次数据更改时, child.component
都会更改 stats
中的值 . 我希望在父级的 <span *ngIf="stats.dataSize > 0">Works</span>
中使用此值 dataSize
. 出于某种原因 *ngIf
不会更新 . 模板 {{stats.dataSize > 0}}
否则更新没有问题 .
我注意到的:如果我删除父级的 OnPush
,Angular将抛出异常 Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.
. 我想这是来自 *ngIf="stats.dataSize > 0"
首先是假的,现在在开发模式下进行第二次变化检测迭代后是真的 .
这就是为什么我在 handleStatsChange
中尝试在父设置中设置 this.cdr.markForCheck();
. handleStatsChange
将在孩子中被召唤 . 这没有任何后果,无论如何抛出异常 .
我想父级的更改检测不会被触发,因为父级没有更改@Input,因此ngIf不会更新?单击按钮 two 次实际显示 Works
. 我这是因为新的摘要周期现在开始(由一个事件触发),而父母ChangeDetectorRef现在正在更新模板?
So why does Angular update {{stats.dataSize > 0}} and throw an error at ngIf?
任何帮助非常感谢:)
2 回答
在Angular中,您无法在更改检测周期中更改值 . 所以,
这就是为什么你在检查后得到关于值被改变的错误的原因 .
进一步挖掘Lifecycle Hooks Documentation我注意到他们提供了关于处理变更检测的非常好的例子 .
在
CounterParentComponent
的反例中,他们必须通过运行新的'tick'来更新它们的LoggerService
,这意味着延迟执行setTimeout
.计数器:
记录:
这完全是我必须做的才能使我的代码工作 . 他们还在他们的文档中提到了这一点: