Después de crear un elemento de formulario personalizado, noté que cuando me alejaba de una ruta que alojaba ese elemento de formulario personalizado, Angular llamaba al método ngOnDestroy
del elemento de formulario y luego llamaba a los métodos registerOnChange
y registerOnTouched
del elemento de formulario. No estoy seguro de por qué llamaría a estos dos métodos en este momento, pero definitivamente no entiendo por qué los llamaría después de ngOnDestroy
.
He creado una reproducción mínima de este error aquí: https://stackblitz.com/edit/angular-ivy-6jhgh6
Este comportamiento es problemático para mí porque en mi aplicación real, destruyo una instancia de un editor de CodeMirror y establezco la referencia de la variable en null
, pero en el método registerOnTouched
, hago referencia a esa variable. Esto da como resultado un error de referencia nula. Esto comenzó a suceder después de actualizar a Angular 12.
Llamar a los métodos registerOnChange
y registerOnTouched
después de que se haya invocado el gancho ngOnDestroy
del componente es una consecuencia de llamar a FormControlName.ngOnDestroy()
.
Cuando se destruye una vista, mi entendimiento de esta función es que se invocarán todos los ganchos onDestroy
de la vista destruida. Entonces, es por eso que las directivas de formulario de esa vista también tendrán su onDestroy
llamado.
Esto es lo que sucede en FormControlName.ngOnDestroy()
:
// `formDirective` refers to the parent `FormGroup` directive this.formDirective.removeControl(this);
Esto es lo que sucede en FormGroupDirective.removeControl()
: :
removeControl(dir: FormControlName): void { cleanUpControl(dir.control || null, dir, /* validateControlPresenceOnChange */ false); removeListItem(this.directives, dir); }
Y finalmente, cleanUpControl
es desde donde se invocan los métodos registerOnChange
y 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); } /* .... */ }
Entonces, según tengo entendido, es parte del mecanismo de limpieza . De hecho, es un poco inconveniente porque esos métodos se llaman cuando se configura la vista y también cuando se destruye la vista. Supongo que la solución es verificar primero los valores nulos/indefinidos.
Si desea explorar más, puede usar la palabra clave del debugger
:
registerOnChange(fn: any): void { debugger; this.con.log(`registerOnChange (${this.id})`); }
y luego abra DevTools en el proyecto StackBlitz.