¿Por qué es el componente en este simple golpe?
@Component({
selector: 'my-app',
template: `<div>I'm {{message}} </div>`,
})
export class App {
message:string = 'loading :(';
ngAfterViewInit() {
this.updateMessage();
}
updateMessage(){
this.message = 'all done loading :)'
}
}
lanzamiento:
EXCEPCIÓN: La expresión 'I'm {{message}} en la aplicación @ 0: 5' ha cambiado después de que se verificó. Valor anterior: 'Estoy cargando :('. Valor actual: 'Ya terminé de cargar :)' en [I'm {{message}} en App @ 0: 5]
cuando todo lo que estoy haciendo es actualizar un enlace simple cuando se inicia mi vista?
typescript
angular
dibujó más
fuente
fuente
ExpressionChangedAfterItHasBeenCheckedError
error explica el comportamiento con gran detalle.ChangeDetectionStrategy
cuando usedetectChanges()
stackoverflow.com/questions/39787038/…Respuestas:
Como lo indicó drewmoore, la solución adecuada en este caso es activar manualmente la detección de cambios para el componente actual. Esto se realiza utilizando el
detectChanges()
método delChangeDetectorRef
objeto (importado deangular2/core
), o sumarkForCheck()
método, que también actualiza los componentes principales. Ejemplo relevante :Aquí también hay Plunkers que demuestran los enfoques ngOnInit , setTimeout y enableProdMode por si acaso.
fuente
cdr : any
o algo así en lamessage
declaración. ¿Solo me preocupa que me haya perdido algo?Primero, tenga en cuenta que esta excepción solo se lanzará cuando esté ejecutando su aplicación en modo de desarrollo (que es el caso de forma predeterminada a partir de beta-0): si llama
enableProdMode()
al arrancar la aplicación, no se lanzará ( consulte plunk actualizado ).En segundo lugar, no haga eso porque esta excepción se está lanzando por una buena razón: en resumen, cuando está en modo dev, cada ronda de detección de cambio es seguida inmediatamente por una segunda ronda que verifica que no hayan cambiado los enlaces desde el final de la primera, ya que esto indicaría que los cambios están siendo causados por la detección de cambios en sí.
En su plunk, el enlace
{{message}}
se cambia por su llamada asetMessage()
, que ocurre en elngAfterViewInit
gancho, que ocurre como parte del turno de detección de cambio inicial. Sin embargo, eso en sí mismo no es problemático: el problema es quesetMessage()
cambia el enlace pero no desencadena una nueva ronda de detección de cambios, lo que significa que este cambio no se detectará hasta que se desencadene una futura ronda de detección de cambios en otro lugar.La conclusión: todo lo que cambie un enlace debe activar una ronda de detección de cambio cuando lo haga.
Actualice en respuesta a todas las solicitudes de un ejemplo de cómo hacerlo : la solución de @ Tycho funciona, al igual que los tres métodos en la respuesta que señaló @MarkRajcok. Pero, francamente, todos se sienten feos y equivocados para mí, como el tipo de trucos a los que nos acostumbramos en ng1.
Para estar seguro, hay ocasionales circunstancias en las que estos cortes son apropiadas, pero si usted está utilizando en cualquier cosa más que una muy forma ocasional, es una señal de que estás luchando contra el marco en lugar de abrazar plenamente su naturaleza reactiva.
En mi humilde opinión, una manera más idiomática, "Angular2" de abordar esto es algo así como: ( plunk )
fuente
detectChanges()
.updateMessage
, nosetMessage
ngAfterViewChecked()
trabajó para mi:fuente
Lo arreglé agregando ChangeDetectionStrategy desde el núcleo angular.
fuente
CheckOnce
( documentación )this.cdr.detectChanges();
después de lo que sea que estés intentando cargar. Porque podría ser porque la detección de cambios no se está activando¿No puedes usar
ngOnInit
porque solo estás cambiando la variable miembromessage
?Si desea acceder a una referencia a un componente secundario
@ViewChild(ChildComponent)
, debe esperar con élngAfterViewInit
.Una solución sucia es llamar
updateMessage()
al siguiente bucle de eventos con, por ejemplo, setTimeout.fuente
Para esto, he intentado las respuestas anteriores, muchas no funcionan en la última versión de Angular (6 o posterior)
Estoy usando el control de material que requirió cambios después de realizar el primer enlace.
Agregando mi respuesta, esto ayuda a algunos a resolver problemas específicos.
fuente
El artículo Todo lo que necesita saber sobre el
ExpressionChangedAfterItHasBeenCheckedError
error explica el comportamiento con gran detalle.El problema con su configuración es que el
ngAfterViewInit
enlace del ciclo de vida se ejecuta después de las actualizaciones DOM procesadas de detección de cambios. Y está cambiando efectivamente la propiedad que se utiliza en la plantilla en este enlace, lo que significa que DOM debe volver a representarse:y esto requerirá otro ciclo de detección de cambio y Angular por diseño solo ejecuta un ciclo de resumen.
Básicamente tienes dos alternativas para solucionarlo:
Actualice la propiedad asincrónicamente utilizando
setTimeout
,Promise.then
o asincrónica observable referenciada en la plantillarealice la actualización de la propiedad en un enlace antes de la actualización DOM: ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.
fuente
Solo tiene que actualizar su mensaje en el enlace correcto del ciclo de vida, en este caso es en
ngAfterContentChecked
lugar dengAfterViewInit
, porque en ngAfterViewInit se ha iniciado una comprobación del mensaje variable pero aún no ha finalizado.ver: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#afterview
entonces el código será solo:
vea la demostración de trabajo en Plunker.
fuente
@ViewChildren()
contador basado en la longitud que estaba poblado por un Observable. ¡Esta fue la única solución que funcionó para mí!ngAfterContentChecked
yChangeDetectorRef
desde arriba funcionó para mí. EnngAfterContentChecked
llamada -this.cdr.detectChanges();
Este error se produce porque el valor existente se actualiza inmediatamente después de la inicialización. Entonces, si actualiza el nuevo valor después de que el valor existente se representa en DOM, entonces funcionará bien. Como se menciona en este artículo Depuración angular "La expresión ha cambiado después de que se verificó"
por ejemplo puedes usar
}
o
Como puede ver, no he mencionado el tiempo en el método setTimeout. Como es la API proporcionada por el navegador, no una API de JavaScript, por lo tanto, esto se ejecutará por separado en la pila del navegador y esperará hasta que finalicen los elementos de la pila de llamadas.
Philip Roberts explica cómo el API evoca el concepto en uno de los videos de Youtube (¿Cuál es el truco del bucle de eventos?).
fuente
También puede poner su llamada a updateMessage () en el método ngOnInt (), al menos funciona para mí
En RC1 esto no desencadena la excepción
fuente
También puede crear un temporizador utilizando la
Observable.timer
función rxjs y luego actualizar el mensaje en su suscripción:fuente
Lanza un error porque su código se actualiza cuando se llama a ngAfterViewInit () . Significa que su valor inicial cambió cuando se produce ngAfterViewInit. Si llama a eso en ngAfterContentInit () , no arrojará un error.
fuente
No pude comentar sobre la publicación de @Biranchi ya que no tengo suficiente reputación, pero me solucionó el problema.
Una cosa a tener en cuenta! Si agregar changeDetection: ChangeDetectionStrategy.OnPush en el componente no funcionó, y es un componente hijo (componente tonto), intente agregarlo también al padre.
Esto solucionó el error, pero me pregunto cuáles son los efectos secundarios de esto.
fuente
Obtuve un error similar al trabajar con datatable. Lo que sucede es que cuando usas * ngFor dentro de otra * ngFor datatable, arroja este error ya que interepta el ciclo de cambio angular. SO, en lugar de usar datatable dentro de datatable, use una tabla regular o reemplace mf.data con el nombre de la matriz. Esto funciona bien
fuente
Creo que la solución más simple sería la siguiente:
(static working: boolean)
en la clase donde existe esta función y cada vez que llame a la función, simplemente haga que sea verdadera lo que quiera. Dentro de la función, si el valor de trabajar es verdadero, simplemente regrese de inmediato sin hacer nada. De lo contrario, realice la tarea que desee. ¡Asegúrese de cambiar esta variable a falso una vez que se complete la tarea, es decir, al final de la línea de códigos o dentro del método de suscripción cuando haya terminado de asignar valores!fuente