首页 > 解决方案 > 由虚拟方法触发的角度变化检测?

问题描述

您好,我尝试了解角度的​​变化检测。

我有一个my-editable用于内联文本编辑的父组件和一个my-input带有样式输入元素的子组件。我正在使用从父组件到子组件的反应式表单。
现在,如果子组件的输出属性status发出一个新事件,我的父组件会调用它的方法dummy()而无需任何表达式。

我感到困惑的是,父母的变化检测显然被触发了!
如果我(status)="dummy()"从模板中my-editable.component.html的错误消息 ( <small class="error">{{ formControl.errors.msg }}</small>) 中删除该行,则不会显示。
在我看来,变更检测应该由(status)="cdr.detectChanges()"类似的东西触发。

谁能给我解释一下?

my-input.component.ts
@Component({
  selector: 'my-input',
  ...
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyInput implements OnInit, OnDestroy {

  ...

  @Input('control')
  set control(control: AbstractControl) {
    this.formControl = control as FormControl;
    this.formGroup = control.parent;
  }

  @Output('status') statusOutput = new EventEmitter<string>();
  set status(status: string) {
    if (status) {
      this.statusOutput.emit(status);
      this.statusClass = status.toLowerCase();
      this.cdr.detectChanges();
    } else {
      this.statusClass = false;
    }
  }

  public statusClass: false | string;

  public formGroup: FormGroup | FormArray;
  public formControl: FormControl;

  ...

  constructor(
    ...
    private cdr: ChangeDetectorRef
  ) {...}

  public ngOnInit(): void {
    this.status = this.formControl.status;
    this.updateOnStatusChange();
    ...
  }

  ...

  private updateOnStatusChange(): void {
    this.formControl.statusChanges
      .pipe(pairwise(), untilDestroyed(this))
      .subscribe(([lastStatus, status]) => {
        if (status !== lastStatus) {
          this.status = status;
        }
      });
  }
}
my-input.component.html
<i [ngClass]="statusClass" *ngIf="statusClass"></i>
<input [formControl]="formControl" [placeholder]="placeholder">
my-editable.component.ts
@Component({
  selector: 'my-editable',
  ...
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyEditableComponent {

  ...

  @Input('control')
  set control(control: AbstractControl) {
    this.formControl = control as FormControl;
  }

  ...

  public formControl: FormControl;

  ...

  public dummy() {} // <<< MAGIC

  ...

}
my-editable.component.html
...

<ng-container [ngSwitch]="currentMode">

  <ng-template [ngSwitchCase]="EditModes.READONLY">
    <p>{{ formControl.value }}</p>
  </ng-template>

  <ng-template [ngSwitchCase]="EditModes.EDITABLE">
    <div class="wrapper">
      <p>{{ formControl.value }}</p>
      <button (click)="mode = EditModes.EDIT">
        <i class="edit"></i>
      </button>
    </div>
  </ng-template>

  <ng-template [ngSwitchCase]="EditModes.EDIT">
    <my-input
      (status)="dummy()" <!-- MAGIC -->
      [control]="formControl"
      ...
    ></my-input>
  </ng-template>

</ng-container>

<ng-container [ngSwitch]="formControl.invalid && formControl.touched">

  <ng-template [ngSwitchCase]="true">
    <small class="error">{{ formControl.errors.msg }}</small>
  </ng-template>

  <ng-template [ngSwitchCase]="false">
    <small class="description">{{ description }}</small>
  </ng-template>

</ng-container>

标签: angularangular7angular-formsangular-changedetection

解决方案


昨天刚看了这个精彩的演讲。

你应该在 14:30 到 17:00 之间找到答案。

TLDR:当您拥有时OnPush,更改检测将在@Input数据更改时触发,而且在内部事件发生时也会触发。


推荐阅读