为什么 Angular 在自定义 ControlValueAccessor 组件上的 `ngOnDestroy` 之后调用 `registerOnChange`?

Why is Angular calling `registerOnChange` after `ngOnDestroy` on custom ControlValueAccessor component?

创建自定义表单元素后,我注意到当我离开托管该自定义表单元素的路由时,Angular 会调用表单元素的 ngOnDestroy 方法,然后它会调用表单元素的 registerOnChangeregisterOnTouched 方法。我不确定为什么此时会调用这两个方法,但我绝对不明白为什么它会调用它们 after ngOnDestroy.

我在这里创建了这个错误的最小复制:https://stackblitz.com/edit/angular-ivy-6jhgh6

这种行为对我来说是有问题的,因为在我的实际应用程序中,我正在破坏 CodeMirror 编辑器的一个实例并将变量引用设置为 null,但是在 registerOnTouched 方法中,我引用该变量。这会导致空引用错误。升级到 Angular 12.

后才开始发生这种情况

在调用组件的 ngOnDestroy 挂钩后调用 registerOnChangeregisterOnTouched 方法是调用 FormControlName.ngOnDestroy().

的结果

当视图被销毁时,我对 this function 的理解是,将调用被销毁视图中的所有 onDestroy 挂钩。所以,这就是为什么该视图的表单指令也会调用它们的 onDestroy 钩子。

这就是 FormControlName.ngOnDestroy() 上发生的事情:

// `formDirective` refers to the parent `FormGroup` directive
this.formDirective.removeControl(this);

这是 FormGroupDirective.removeControl() 上发生的事情: :

removeControl(dir: FormControlName): void {
  cleanUpControl(dir.control || null, dir, /* validateControlPresenceOnChange */ false);
  removeListItem(this.directives, dir);
}

最后,cleanUpControl 是调用 registerOnChangeregisterOnTouched 方法的地方:

// Reverts configuration performed by the `setUpControl` control function.
// Effectively disconnects form control with a given form directive.
// This function is typically invoked when corresponding form directive is being destroyed.
export function cleanUpControl(/* ... */) {
  /* ... */
  const noop = () => {
    if (validateControlPresenceOnChange && (typeof ngDevMode === 'undefined' || ngDevMode)) {
      _noControlError(dir);
    }
  };

  if (dir.valueAccessor) {
    dir.valueAccessor.registerOnChange(noop);
    dir.valueAccessor.registerOnTouched(noop);
  }
  /* .... */
}

因此,根据我的理解,它是清理机制的一部分。确实有点不方便,因为那些方法在view建立的时候调用,view销毁的时候也调用。我猜解决方案是首先检查 null/undefined 值。

如果您想进一步探索,可以使用 debugger 关键字:

registerOnChange(fn: any): void {
  debugger;
  this.con.log(`registerOnChange (${this.id})`);
}

然后打开 StackBlitz 项目中的 DevTools。