Explícame por qué sigo recibiendo este error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Obviamente, solo lo obtengo en modo de desarrollo, no sucede en mi versión de producción, pero es muy molesto y simplemente no entiendo los beneficios de tener un error en mi entorno de desarrollo que no aparecerá en prod. -probablemente debido a mi falta de comprensión.
Por lo general, la solución es bastante fácil, solo envuelvo el error que causa el código en un setTimeout como este:
setTimeout(()=> {
this.isLoading = true;
}, 0);
O forzar la detección de cambios con un constructor como este constructor(private cd: ChangeDetectorRef) {}
:
this.isLoading = true;
this.cd.detectChanges();
Pero, ¿por qué me encuentro constantemente con este error? Quiero entenderlo para poder evitar estas soluciones hacky en el futuro.
fuente
Respuestas:
Tuve un problema similar En cuanto a la documentación de los ganchos del ciclo de vida , he cambiado
ngAfterViewInit
angAfterContentInit
y funcionó.fuente
setTimeout
como muestra arriba hizo el truco. ¡Gracias!ngAfterContentChecked
funciona aquí mientrasngAfterContentInit
aún arroja error.Este error indica un problema real en su aplicación, por lo tanto, tiene sentido lanzar una excepción.
En
devMode
detección de cambios, se agrega un giro adicional después de cada ejecución de detección de cambios regular para verificar si el modelo ha cambiado.Si el modelo ha cambiado entre el turno de detección de cambio regular y el adicional, esto indica que
ambos son malos, porque no está claro cómo proceder porque el modelo podría nunca estabilizarse.
Si Angular ejecuta la detección de cambios hasta que el modelo se estabilice, podría ejecutarse para siempre. Si Angular no ejecuta la detección de cambios, entonces la vista podría no reflejar el estado actual del modelo.
Consulte también ¿Cuál es la diferencia entre el modo de producción y desarrollo en Angular2?
fuente
ngOnInit
ongOnChanges
para modificar el modelo (algunas devoluciones de llamada del ciclo de vida permiten modificar el modelo, otras no, no me recuerdo exactamente cuál hacer o no). No se vincule a métodos o funciones en la vista, en lugar de hacerlo, a campos y actualice los campos en los controladores de eventos. Si debe vincularse a los métodos, asegúrese de que siempre devuelvan la misma instancia de valor siempre que no haya realmente un cambio. La detección de cambios llamará mucho a estos métodos.changeRef.detectChanges()
es una solución / suprime el error, es prueba de ello. Es como modificar el estado$scope.$watch()
en Angular 1.cdRef.detectChanges()
solo es necesario en algunos casos extraños y debes mirar cuidadosamente cuando lo necesites para entender bien por qué.Mucho entendimiento llegó una vez que entendí los Angular Lifecycle Hooks y su relación con la detección de cambios.
Estaba tratando de hacer que Angular actualizara una bandera global vinculada a
*ngIf
un elemento, y estaba tratando de cambiar esa bandera dentro delngOnInit()
gancho del ciclo de vida de otro componente.Según la documentación, se llama a este método después de que Angular ya haya detectado cambios:
Por lo tanto, actualizar el indicador dentro de
ngOnChanges()
no iniciará la detección de cambios. Luego, una vez que la detección de cambios se ha activado naturalmente nuevamente, el valor de la bandera ha cambiado y se produce el error.En mi caso, cambié esto:
A esto:
y solucionó el problema :)
fuente
router.navigate
) al cargarlo en un fragmento si estaba presente en la URL. Este código se colocó inicialmente en el lugarAfterViewInit
donde recibía el error, luego me moví como le dijiste al constructor, pero no estaba respetando el fragmento. Pasando angOnInit
resolverse :) gracias!setInterval()
también funciona si necesita dispararse después de otro código de evento de por vida.Actualizar
Recomiendo encarecidamente comenzar primero con la respuesta propia del OP : piense correctamente en lo que se puede hacer en
constructor
comparación con lo que se debe hacer enngOnChanges()
.Original
Esto es más una nota al margen que una respuesta, pero podría ayudar a alguien. Me topé con este problema al intentar hacer que la presencia de un botón dependiera del estado del formulario:
Hasta donde yo sé, esta sintaxis hace que el botón se agregue y elimine del DOM según la condición. Lo que a su vez conduce a la
ExpressionChangedAfterItHasBeenCheckedError
.La solución en mi caso (aunque no pretendo comprender todas las implicaciones de la diferencia), fue usar
display: none
en su lugar:fuente
[hidden]
lugar de la parte muy detallada[style.display]
. :)[hidden]
no siempre tendrá el mismo comportamiento quedisplay: none
Hubo respuestas interesantes, pero no encontré una para satisfacer mis necesidades, la más cercana de @ chittrang-mishra, que se refiere solo a una función específica y no a varios conmutadores como en mi aplicación.
No quería usar
[hidden]
para aprovechar*ngIf
ni siquiera ser parte del DOM, así que encontré la siguiente solución, que puede no ser la mejor para todos, ya que suprime el error en lugar de corregirlo, pero en mi caso, donde sé el El resultado final es correcto, parece estar bien para mi aplicación.Lo que hice fue implementar
AfterViewChecked
, agregarconstructor(private changeDetector : ChangeDetectorRef ) {}
y luegoEspero que esto ayude a otros como muchos otros me han ayudado.
fuente
Angular ejecuta la detección de cambios y cuando encuentra que algunos valores que se han pasado al componente secundario han cambiado, Angular arroja el error:
ExpressionChangedAfterItHasBeenCheckedError
haga clic para másPara corregir esto, podemos usar el
AfterContentChecked
gancho del ciclo de vida yfuente
En mi caso, tuve este problema en mi archivo de especificaciones, mientras ejecutaba mis pruebas.
Tuve que cambiar
ngIf
a[hidden]
a
fuente
[hidden]
: talkingdotnet.com/dont-use-hidden-attribute-angularjs-2*ngIf
cambia el DOM, agrega y elimina el elemento de la página, mientras que[hidden]
cambia la visibilidad del elemento sin eliminarlo del DOM.Siga los pasos a continuación:
1. Use 'ChangeDetectorRef' importándolo de @ angular / core de la siguiente manera:
2. Impleméntelo en constructor () de la siguiente manera:
3. Agregue el siguiente método a su función que está llamando en un evento como hacer clic en el botón. Entonces se ve así:
fuente
Estaba usando ng2-carouselamos (Angular 8 y Bootstrap 4)
A continuación se solucionó mi problema:
Lo que hice:
fuente
Estaba enfrentando el mismo problema ya que el valor estaba cambiando en una de las matrices de mi componente. Pero en lugar de detectar los cambios en el cambio de valor, cambié la estrategia de detección de cambio de componente a
onPush
(que detectará cambios en el cambio de objeto y no en el cambio de valor).fuente
En referencia al artículo https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Por lo tanto, la mecánica detrás de la detección de cambios en realidad funciona de manera que tanto los resúmenes de detección de cambios como los de verificación se realizan sincrónicamente. Eso significa que si actualizamos las propiedades de forma asincrónica, los valores no se actualizarán cuando se ejecute el bucle de verificación y no obtendremos ningún
ExpressionChanged...
error. La razón por la que obtenemos este error es que, durante el proceso de verificación, Angular ve valores diferentes de los que registró durante la fase de detección de cambios. Entonces para evitar eso ...1) Use changeDetectorRef
2) usa setTimeOut. Esto ejecutará su código en otra VM como una macro tarea. Angular no verá estos cambios durante el proceso de verificación y no obtendrá ese error.
3) Si realmente desea ejecutar su código en la misma VM, use como
Esto creará una microtarea. La cola de micro tareas se procesa después de que el código síncrono actual ha terminado de ejecutarse, por lo tanto, la actualización de la propiedad se realizará después del paso de verificación.
fuente
@HostBinding
puede ser una fuente confusa de este error.Por ejemplo, supongamos que tiene el siguiente enlace de host en un componente
Para simplificar, digamos que esta propiedad se actualiza mediante la siguiente propiedad de entrada:
En el componente principal lo está configurando programáticamente
ngAfterViewInit
Esto es lo que pasa:
carousel
(a través de ViewChild)carousel
hastangAfterViewInit()
(será nulo)style_groupBG = 'red'
background: red
en el componente host ImageCarouselcarousel.style.background
y no es lo suficientemente inteligente como para saber que esto no es un problema, por lo que arroja la excepción.Una solución es introducir otro contenedor de información privilegiada ImageCarousel y establecer el color de fondo en eso, pero luego no obtendrá algunos de los beneficios del uso
HostBinding
(como permitir que el padre controle los límites completos del objeto).La mejor solución, en el componente principal es agregar detectChanges () después de configurar la configuración.
Esto puede parecer bastante obvio como este, y muy similar a otras respuestas, pero hay una sutil diferencia.
Considere el caso en el que no agrega
@HostBinding
hasta más tarde durante el desarrollo. De repente, obtienes este error y no parece tener ningún sentido.fuente
Aquí están mis pensamientos sobre lo que está sucediendo. No he leído la documentación, pero estoy seguro de que esto es parte de por qué se muestra el error.
Cuando se usa * ngIf, cambia físicamente el DOM al agregar o eliminar el elemento cada vez que cambia la condición. Entonces, si la condición cambia antes de que se muestre en la vista (que es muy posible en el mundo de Angular), se produce el error. Vea la explicación aquí entre los modos de desarrollo y producción.
Cuando se usa
[hidden]
, no cambia físicamenteDOM
sino que simplemente lo ocultaelement
de la vista, lo más probable es que lo useCSS
en la parte posterior. El elemento todavía está allí en el DOM pero no es visible dependiendo del valor de la condición. Es por eso que el error no ocurrirá al usar[hidden]
.fuente
isProcessing()
está haciendo lo correcto, debe usarlo!isProcessing()
para[hidden]
hidden
no "usa CSS en la parte posterior", es una propiedad HTML normal. developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/…Para mi problema, estaba leyendo github - "ExpressionChangedAfterItHasBeenCheckedError al cambiar el valor de un componente 'no modelo' en afterViewInit" y decidí agregar el ngModel
Se solucionó mi problema, espero que ayude a alguien.
fuente
ngModel
? ¿Y podría explicar por qué esto debería ser útil?Consejos de depuración
Este error puede ser bastante confuso, y es fácil hacer una suposición errónea sobre cuándo exactamente está ocurriendo. Encuentro útil agregar muchas declaraciones de depuración como esta en todos los componentes afectados en los lugares apropiados. Esto ayuda a entender el flujo.
En el padre, ponga declaraciones como esta (la cadena exacta 'EXPRESSIONCHANGED' es importante), pero aparte de eso, estos son solo ejemplos:
En las devoluciones de llamada de niño / servicios / temporizador:
Si ejecuta
detectChanges
manualmente, agregue el registro para eso también:Luego, en el depurador de Chrome, simplemente filtre por 'EXPRESSIONCHANGES'. Esto le mostrará exactamente el flujo y el orden de todo lo que se establece, y también exactamente en qué punto Angular arroja el error.
También puede hacer clic en los enlaces grises para colocar puntos de interrupción.
Otra cosa a tener en cuenta si tiene propiedades con nombres similares en toda su aplicación (como
style.background
) asegúrese de depurar la que cree, configurándola en un valor de color oscuro.fuente
En mi caso, tenía una propiedad asíncrona
LoadingService
con un BehavioralSubjectisLoading
El uso del modelo [oculto] funciona, pero * ngIf falla
fuente
Una solución que funcionó para mí usando rxjs
fuente
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
cuando los cambios de valor se activan cuando cambia el DOMdelay
hace que el error desaparezca. Funciona de manera similar asetTimeout
.Tuve este tipo de error en Ionic3 (que usa Angular 4 como parte de su pila de tecnología).
Para mí estaba haciendo esto:
<ion-icon [name]="getFavIconName()"></ion-icon>
Así que estaba tratando de cambiar condicionalmente el tipo de un ícono iónico de a
pin
a aremove-circle
, según el modo en que funcionaba una pantalla.Supongo que tendré que agregar un
*ngIf
en su lugar.fuente
Mi problema fue manifiesto cuando agregué
*ngIf
pero esa no fue la causa. El error fue causado al cambiar el modelo en las{{}}
etiquetas y luego tratar de mostrar el modelo modificado en la*ngIf
declaración posterior. Aquí hay un ejemplo:Para solucionar el problema, cambié donde llamé
changeMyModelValue()
a un lugar que tenía más sentido.En mi situación, quería que me
changeMyModelValue()
llamaran cada vez que un componente secundario cambiaba los datos. Esto requirió que creara y emitiera un evento en el componente hijo para que el padre pudiera manejarlo (llamandochangeMyModelValue()
. Ver https://angular.io/guide/component-interaction#parent-listens-for-child-eventfuente
Espero que esto ayude a alguien que viene aquí: hacemos llamadas de servicio
ngOnInit
de la siguiente manera y usamos una variabledisplayMain
para controlar el Montaje de los elementos en el DOM.componente.ts
y component.html
fuente
Recibí este error porque estaba usando una variable en component.html que no fue declarada en component.ts. Una vez que eliminé la parte en HTML, este error desapareció.
fuente
Recibí este error porque estaba enviando acciones redux en modal y modal no estaba abierto en ese momento. Estaba enviando acciones en el momento en que el componente modal recibe la entrada. Así que puse setTimeout allí para asegurarme de que modal está abierto y luego las acciones están dipatched.
fuente
Para cualquiera que esté luchando con esto. Aquí hay una manera de depurar correctamente este error: https://blog.angular-university.io/angular-debugging/
En mi caso, de hecho me libré de este error usando este truco [oculto] en lugar de * ngSi ...
Pero el enlace que proporcioné me permitió encontrar EL CULPABLE * ngIf :)
Disfrutar.
fuente
hidden
lugar dengIf
no es un truco, ni aborda el núcleo del problema en absoluto. Solo estás ocultando el problema.La solución ... los servicios y los rxjs ... los emisores de eventos y el enlace de propiedad usan rxjs ... es mejor implementarlo usted mismo, más control, más fácil de depurar. Recuerde que los emisores de eventos están utilizando rxjs. Simplemente, cree un servicio y dentro de un observable, haga que cada componente se suscriba al observador y pase un nuevo valor o un valor de costo según sea necesario
fuente