Expression has changed after it was checked: cómo vencer el error en Angular

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

«Expression has changed after it was checked» — ¿por qué ocurre y cómo vivir con ello?

\n\n

Si alguna vez has escrito una aplicación en Angular, seguramente te has topado con este siniestro mensaje en la consola:

\n\n
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.
\n\n

Este error es un verdadero «azote» para los desarrolladores. Miles de preguntas en Stack Overflow, cientos de horas de depuración, decenas de crisis nerviosas. Pero la buena noticia es: no solo se puede corregir, sino también entender por qué ocurre. En este artículo analizaremos las principales causas del error Expression has changed after it was checked, escribiremos ejemplos en TypeScript y daremos soluciones concretas. ¡Allá vamos!

\n\n

¿Qué significa realmente este error?

\n\n

Angular funciona en dos modos: desarrollo (dev mode) y producción. En el modo dev, el framework realiza una verificación adicional: después de cada ciclo de detección de cambios, ejecuta una segunda pasada para asegurarse de que los datos no hayan cambiado «sobre la marcha». Si el valor cambió entre la primera y la segunda pasada, Angular falla con el error ExpressionChangedAfterItHasBeenCheckedError.

\n\n

En palabras simples: le dijiste a Angular «estos son los datos», los verificó, y luego los cambiaste. Angular se ofende y grita.

\n\n

Las 3 principales causas del error

\n\n

1. Cambio de datos en hooks del ciclo de vida después de ngAfterViewInit

\n\n

La causa más popular es intentar cambiar una propiedad del componente en el hook ngAfterViewInit, ngAfterContentInit o ngAfterViewChecked. En ese momento, Angular ya ha completado la verificación de la vista, y cualquier cambio provoca el error.

\n\n

Ejemplo en TypeScript:

\n\n
import { Component, AfterViewInit } from '@angular/core';\n\n@Component({\n  selector: 'app-example',\n  template: '<p>{{ message }}</p>'\n})\nexport class ExampleComponent implements AfterViewInit {\n  message = 'Valor inicial';\n\n  ngAfterViewInit(): void {\n    // ¡Error! Cambiamos la propiedad después de verificar la vista\n    this.message = 'Valor modificado';\n  }\n}
\n\n

Aquí Angular primero verifica message (valor «Valor inicial»), luego llama a ngAfterViewInit, donde lo cambias a «Valor modificado». En la segunda verificación, Angular ve la discrepancia y lanza el error.

\n\n

2. Operaciones asíncronas dentro del ciclo de vida

\n\n

Si ejecutas código asíncrono (setTimeout, Promise, Observable) dentro de un hook, la actualización de datos ocurre fuera de la zona de control de Angular.

\n\n

Ejemplo:

\n\n
import { Component, OnInit } from '@angular/core';\n\n@Component({\n  selector: 'app-async',\n  template: '<p>{{ data }}</p>'\n})\nexport class AsyncComponent implements OnInit {\n  data = 'Inicio';\n\n  ngOnInit(): void {\n    setTimeout(() => {\n      this.data = 'Nuevo valor';\n    }, 0);\n  }\n}
\n\n

Aunque setTimeout con retardo de 0 ms se ejecuta casi de inmediato, Angular ya ha completado la verificación. En modo dev, esto provocará el error.

\n\n

3. Componentes anidados y paso de datos mediante Input

\n\n

Cuando el componente padre pasa datos al hijo mediante @Input, y el componente hijo intenta cambiar esos datos en su ciclo de vida, surge un conflicto.

\n\n

Ejemplo:

\n\n
// Componente padre\n@Component({\n  selector: 'app-parent',\n  template: '<app-child [value]="parentValue"></app-child>'\n})\nexport class ParentComponent {\n  parentValue = 'Original';\n}\n\n// Componente hijo\n@Component({\n  selector: 'app-child',\n  template: '<p>{{ value }}</p>'\n})\nexport class ChildComponent implements AfterViewInit {\n  @Input() value: string = '';\n\n  ngAfterViewInit(): void {\n    this.value = 'Modificado en                

Blogs

Book Recommendations