Angular 变化检测 NgIf

标签 angular angular2-changedetection

所以我试图更好地理解 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/

相关文章:

angular - Observable 类型不存在属性 'then'

node.js - ionic 2 undefined is not an object 错误

Angular:当凭据标志为真时,不能在 '*' header 中使用通配符 'Access-Control-Allow-Origin'

Angular 2 + OnPush : Change detection does not work on pouchdb callbacks

angular - 即使输入未更改,在更改检测周期期间也会使用 OnPush 策略更新 View 的 DOM

javascript - Angular ngx-select-dropdown : dropdown options not updating

angular - 如何在 Angular 中将表单控件重置为原始状态?

angular - 如何防止 Firebase 在 Angular 2 中重复触发变更检测?

动态组件的 Angular 2 OnPush 变化检测

javascript - 从 Electron Container IPC Channel 接收数据时,变更检测会间歇性工作