gpt4 book ai didi

Angular 变化检测 NgIf

转载 作者:行者123 更新时间:2023-12-05 03:09:26 26 4
gpt4 key购买 nike

所以我试图更好地理解 Angulars ChangeDetection 并无意中遇到了一个问题: https://plnkr.co/edit/M8d6FhmDhGWIvSWNVpPm?p=preview

这个 Plunkr 是我的应用程序代码的简化版本,基本上有一个parent 和一个child 组件。两者都有 ChangeDetectionStrategy.OnPush启用。

父组件.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();
}
}

子组件.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);
}
}

所以 parent 更新data单击按钮触发 ngOnChanges在 child 。每次数据更改时,child.component更改 stats 中的值.我想要这个值,dataSize , 用于 <span *ngIf="stats.dataSize > 0">Works</span>在 parent 。出于某种原因 *ngIf不会更新。模板{{stats.dataSize > 0}}否则更新没问题。

我注意到:如果我删除 OnPush在父级上,Angular 将抛出异常 Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'. .我想这来自 *ngIf="stats.dataSize > 0"在开发模式下进行第二次变更检测迭代后,首先为假,现在为真。

这就是我尝试设置 this.cdr.markForCheck(); 的原因在 parent 中 handleStatsChange . handleStatsChange将被称为 child 。这没有任何后果,无论如何都会抛出异常。

我猜父项上的变化检测不会被触发,因为父项中没有更改@Input,因此 ngIf 不会更新?点击按钮两次实际上会显示Works .我这是因为新的摘要周期现在开始(由事件触发)并且 parent ChangeDetectorRef 现在正在更新模板?

那么为什么 Angular 会更新 {{stats.dataSize > 0}}并在 ngIf 处抛出错误?

非常感谢任何帮助:)

最佳答案

我今天遇到 intersection observer 时遇到这个问题,其中将项目滚动到 View 中应该会执行某些操作,并且 99% 的时间它会执行,但 1% 的时间它不会触发。

重要:异步管道实际上在内部调用了markForCheck。你可以看看the source code (接近尾声)。

我过去使用可观察对象解决此问题的一个解决方案是创建一个创建“延迟”的管道。这有助于解决“表达式已更改”错误,因为它有效地导致了新的变化检测器循环。

这应该是最后的手段 - 但我已经用过几次,因为我无法在其他地方获得正确的时机。

<div *ngIf="model.showPanel | delayTime | async">...</div>

默认值为 0ms,delay() RxJS 管道使用 setTimeout:

@Pipe({
name: 'delayTime'
})
export class DelayTimePipe implements PipeTransform {

constructor() {}

transform(value?: Observable<any> | any, ms?: number): Observable<any> {
if (isObservable(value)) {
return value.pipe(delay(ms || 0));
} else {
throw debuggerError('[DelayTimePipe] Needs to be an observable');
}
}
}

关于 Angular 变化检测 NgIf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42861377/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com