Cuando trabajamos con Angular, a veces nos enfrentamos a errores que pueden ser difíciles de interpretar, especialmente cuando se trata de la detección de cambios. Uno de estos errores es el famoso ExpressionChangedAfterItHasBeenCheckedError. A continuación, te explico en qué consiste este error, por qué sucede y cómo solucionarlo con un caso práctico relacionado con la configuración de ordenamiento en un componente.
Este error ocurre cuando Angular detecta que el valor de una expresión ha cambiado después de que el ciclo de detección de cambios ya ha sido completado. Esto puede suceder en ciertos hooks del ciclo de vida de Angular, como ngAfterViewInit
o ngAfterViewChecked
.
La detección de cambios en Angular es fundamental para su capacidad de actualización automática de vistas, pero si los datos cambian una vez que la vista ha sido verificada, Angular lanza este error para evitar inconsistencias.
En este caso, el error se originó en un componente SearchDialogComponent, y estaba relacionado con la configuración de ordenamiento (sorting). El código fallaba porque, después de que Angular completaba la detección de cambios, los valores de algunos atributos cambiaban, lo que desataba el error:
El error ocurre porque, durante la ejecución de ngAfterViewInit()
, Angular ya ha terminado su ciclo de detección de cambios, pero al intentar configurar el ordenamiento en ese mismo ciclo, estamos cambiando el estado de la vista, lo que provoca el error.
setTimeout
Para resolver este problema, podemos usar setTimeout
. Aunque parezca extraño, este método es muy útil en estos casos. Al envolver el código problemático dentro de un setTimeout
, retrasamos su ejecución hasta después de que Angular haya terminado su ciclo de detección de cambios actual. Esto garantiza que cualquier cambio en la vista o los datos no interfiera con el ciclo de verificación.
Ejemplo de solución:
ngAfterViewInit() {
// Envuelve estas llamadas en un setTimeout para evitar el error
setTimeout(() => {
this.getArray(this.filter);
this.configureSorting();
});
}
El uso de setTimeout(() => { ... }, 0)
asegura que el código que manipula el estado del componente se ejecute en la siguiente "vuelta" del bucle de eventos de JavaScript, después de que Angular haya completado su ciclo actual de detección de cambios. Esto permite que Angular actualice correctamente los cambios realizados sin que se detecte el problema en la expresión.
En el caso de este componente, también fue necesario modificar la lógica de ordenamiento. Aquí está el ejemplo de cómo se ajustó el código:
configureSorting() {
this.dataSource.sort = this.sort;
// En lugar de configurar el estado de ordenamiento manualmente, usamos setTimeout
setTimeout(() => {
this.sort.sort({ id: 'check', start: 'desc' } as MatSortable);
});
this.sort.sortChange.subscribe((sortState) => {
if (sortState.active === 'check' && sortState.direction === '') {
this.sort.direction = 'asc';
}
});
this.dataSource.sortingDataAccessor = (item, property) => {
if (property === 'check') {
return item.isChecked ? 1 : 0;
}
return item[property];
};
}
setTimeout
Existen otras soluciones, como usar el ChangeDetectorRef.detectChanges()
para forzar la detección de cambios o NgZone.run()
para ejecutar código dentro del contexto de la zona de Angular. Sin embargo, el uso de setTimeout
es una solución rápida y efectiva en la mayoría de los casos para este tipo de errores.
El error ExpressionChangedAfterItHasBeenCheckedError puede ser confuso al principio, pero tiene solución. Al usar setTimeout
, le permitimos a Angular finalizar su ciclo de detección de cambios antes de realizar modificaciones en los datos o la vista, evitando así este error. Si te enfrentas a este problema en tu propio código, prueba con esta solución y asegúrate de aplicar los cambios después de que Angular haya terminado de procesar la vista actual.
I am particularly drawn to developing applications that are not only functional but also visually appealing and easy to use. I accomplish this by implementing SOLID principles and clean architecture, and applying testing to ensure quality.