true previous has false expressionchangedafterithasbeencheckederror error current changed been avoid after typescript angular

typescript - previous - expressionchangedafterithasbeencheckederror ngif



La expresión___ ha cambiado después de que se verificó (12)

¿Por qué es el componente en este simple plunk

@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?


¿No puede usar ngOnInit porque solo está cambiando el message variable miembro?

Si desea acceder a una referencia a un componente hijo @ViewChild(ChildComponent) , debe esperar con ngAfterViewInit .

Una solución sucia es llamar a updateMessage() en el siguiente bucle de eventos con, por ejemplo, setTimeout.

ngAfterViewInit() { setTimeout(() => { this.updateMessage(); }, 1); }


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 método detectChanges() del objeto ChangeDetectorRef (importado de angular2/core ), o su método markForCheck() , que también actualiza los componentes principales. example relevante:

import { Component, ChangeDetectorRef, AfterViewInit } from ''angular2/core'' @Component({ selector: ''my-app'', template: `<div>I''m {{message}} </div>`, }) export class App implements AfterViewInit { message: string = ''loading :(''; constructor(private cdr: ChangeDetectorRef) {} ngAfterViewInit() { this.message = ''all done loading :)'' this.cdr.detectChanges(); } }

Aquí también hay Plunkers que demuestran los ngOnInit , setTimeout y enableProdMode si acaso.


El artículo Todo lo que necesita saber sobre el error ExpressionChangedAfterItHasBeenCheckedError explica el comportamiento con gran detalle.

El problema con su configuración es que el ngAfterViewInit ciclo de vida ngAfterViewInit 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:

ngAfterViewInit() { this.message = ''all done loading :)''; // needs to be rendered the DOM }

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:

  • setTimeout la propiedad asincrónicamente usando setTimeout , Promise.then o asincrónica observable referenciada en la plantilla

  • realice la actualización de la propiedad en un enlace antes de la actualización DOM: ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked.


Lo arreglé agregando ChangeDetectionStrategy desde el núcleo angular.

import { Component, ChangeDetectionStrategy } from ''@angular/core''; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: ''page1'', templateUrl: ''page1.html'', })


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.


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.

export class AbcClass implements OnInit, AfterContentChecked{ constructor(private ref: ChangeDetectorRef) {} ngOnInit(){ // your tasks } ngAfterViewInit() { this.ref.detectChanges(); } }

Agregando mi respuesta, esto ayuda a algunos a resolver problemas específicos.


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 a enableProdMode() al enableProdMode() la aplicación, no se obtendrá arrojado ( ver 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, como esto indicaría que los cambios están siendo causados ​​por la detección de cambios en sí.

En su plunk, el enlace {{message}} es cambiado por su llamada a setMessage() , lo que ocurre en el ngAfterViewInit , 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 que setMessage() 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 active alguna futura ronda de detección de cambios en otro lugar .

La conclusión: cualquier cosa 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.

Sin duda, hay circunstancias ocasionales en las que estos hacks son apropiados, pero si los está utilizando de una forma más que ocasional, es una señal de que está luchando contra el marco en lugar de adoptar plenamente su naturaleza reactiva.

En mi humilde opinión, una forma más idiomática, "Angular2" de abordar esto es algo así como: ( plunk )

@Component({ selector: ''my-app'', template: `<div>I''m {{message | async}} </div>` }) export class App { message:Subject<string> = new BehaviorSubject(''loading :(''); ngAfterViewInit() { this.message.next(''all done loading :)'') } }


Solo tiene que actualizar su mensaje en el ngAfterContentChecked correcto del ciclo de vida, en este caso es ngAfterContentChecked lugar de ngAfterViewInit , 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:

import { Component } from ''angular2/core'' @Component({ selector: ''my-app'', template: `<div>I''m {{message}} </div>`, }) export class App { message: string = ''loading :(''; ngAfterContentChecked() { this.message = ''all done loading :)'' } }

vea la demostración de trabajo en Plunker.


También puede crear un temporizador usando la función rxjs Observable.timer y luego actualizar el mensaje en su suscripción:

Observable.timer(1).subscribe(()=> this.updateMessage());


También puede poner su llamada a updateMessage () en el método ngOnInt (), al menos funciona para mí

ngOnInit() { this.updateMessage(); }

En RC1 esto no desencadena la excepción


ngAfterViewChecked() funcionó para mí:

import { Component, ChangeDetectorRef } from ''@angular/core''; //import ChangeDetectorRef constructor(private cdr: ChangeDetectorRef) { } ngAfterViewChecked(){ //your code to update the model this.cdr.detectChanges(); }


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.

ngAfterContentInit() { this.updateMessage(); }