使用 ChangeDetectionStrategy.OnPush 在 setTimeout 中不会触发更改检测

Ale*_*ore 5 angular

我有两个组件,我在其中使用 ChangeDetectionStrategy.OnPush。父组件:

    @Component({
     changeStaregy: ChangeDetectionStrategy.OnPush,
     template:`
        <button (click)="onClick()">clear</button>
        <div>
            <test [model]="model"></test>
        </div>`
    })
    export class AppComponent {
        model: TestModel;
        
        constructor(){
          this.model = { id: 1, text: 'bla bla bla'}
        }
    
        onClick() {
          this.model = new TestModel();
        }
    }
Run Code Online (Sandbox Code Playgroud)

和一个只显示数据的子组件:

    @Component({
       changeStrategy: ChangeDetectionStrategy.OnPush,
       selector: 'test',
       template: `
        <div>
              <div> {{model.id}} </div>
               <div> {{model.text}} </div>
        </div>`
    })
    
    export class TestComponent {
       @Input() model: TestModel;
       
    }
Run Code Online (Sandbox Code Playgroud)

当我单击“清除”按钮时,它会调用 onClick() 函数,该函数将一个空实体分配给“模型”。这会触发更改检测,因为输入已更改(OnPush 策略)。但是,如果我使用异步调用包装分配,则更改检测不起作用,因此 UI 不会更新:

    onClick() {
      setTimeout(() => {
        this.model = new TestModel();
      }, 2000);
    }
Run Code Online (Sandbox Code Playgroud)

Angular2+ 有 NgZone 修补了 setTimeout 函数。修补后的 setTimeout 必须触发更改检测,但在我的情况下它不会。为什么更改检测不起作用?我该如何解决?

Ahb*_*ert 6

以下是您的场景中发生的情况的步骤:

  1. 绑定的单击事件在触发时会将组件及其祖先标记为脏。这发生在onClick()方法执行之前。
  2. onClick()方法执行后,Angular 会调用ApplicationRef.tick()以启动更改检测。由于祖先组件被标记为脏,因此更改检测会发生在 OnPush 组件上。请记住,此时 setTimeout 回调尚未执行。
  3. 更改检测完成后,组件不再“脏”。然后执行 setTimeout 回调。setTimeout 回调完成后,调用 ApplicationRef.tick() 再次启动更改检测。但是,由于您的 AppComponent 标记为 OnPush,并且没有任何@Input更改(因为 AppComponent 没有输入),并且组件及其祖先未标记为脏,因此更改检测不会传递到子组件。所以对 的更改this.model不会反映在 DOM 中。

我发现这很有帮助: https://www.mokkaapps.de/blog/the-last-guide-for-angular-change-detection-you-will-ever-need


Pie*_*Duc 5

因为在父元素上changeDetectionStrategy设置为OnPush,所以更改检测周期在父元素处停止。因此OnPush,无论其自己的设置如何,此父级的任何子级都将策略设置为。

@Input父节点没有任何变化,因此变化检测器不会深入到父节点的子节点。您应该执行 adetectChanges以使更改生效