为什么 Angular 在自定义 ControlValueAccessor 组件上的 `ngOnDestroy` 之后调用 `registerOnChange`?
Why is Angular calling `registerOnChange` after `ngOnDestroy` on custom ControlValueAccessor component?
创建自定义表单元素后,我注意到当我离开托管该自定义表单元素的路由时,Angular 会调用表单元素的 ngOnDestroy
方法,然后它会调用表单元素的 registerOnChange
和 registerOnTouched
方法。我不确定为什么此时会调用这两个方法,但我绝对不明白为什么它会调用它们 after ngOnDestroy
.
我在这里创建了这个错误的最小复制:https://stackblitz.com/edit/angular-ivy-6jhgh6
这种行为对我来说是有问题的,因为在我的实际应用程序中,我正在破坏 CodeMirror 编辑器的一个实例并将变量引用设置为 null
,但是在 registerOnTouched
方法中,我引用该变量。这会导致空引用错误。升级到 Angular 12.
后才开始发生这种情况
在调用组件的 ngOnDestroy
挂钩后调用 registerOnChange
和 registerOnTouched
方法是调用 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
是调用 registerOnChange
和 registerOnTouched
方法的地方:
// 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。
创建自定义表单元素后,我注意到当我离开托管该自定义表单元素的路由时,Angular 会调用表单元素的 ngOnDestroy
方法,然后它会调用表单元素的 registerOnChange
和 registerOnTouched
方法。我不确定为什么此时会调用这两个方法,但我绝对不明白为什么它会调用它们 after ngOnDestroy
.
我在这里创建了这个错误的最小复制:https://stackblitz.com/edit/angular-ivy-6jhgh6
这种行为对我来说是有问题的,因为在我的实际应用程序中,我正在破坏 CodeMirror 编辑器的一个实例并将变量引用设置为 null
,但是在 registerOnTouched
方法中,我引用该变量。这会导致空引用错误。升级到 Angular 12.
在调用组件的 ngOnDestroy
挂钩后调用 registerOnChange
和 registerOnTouched
方法是调用 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
是调用 registerOnChange
和 registerOnTouched
方法的地方:
// 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。