tag expressionchangedafterithasbeencheckederror change avoid angular typescript angular-components

avoid - Angular 4 ExpressionChangedAfterItHasBeenCheckedError



expressionchangedafterithasbeencheckederror angular 4 (2)

en ParentComponent =>

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''''. Current value: ''[object Object]''. at viewDebugError (vendor.bundle.js:8962) at expressionChangedAfterItHasBeenCheckedError (vendor.bundle.js:8940)

Componente principal Html

<div> <app-child-widget [allItems]="allItems" (notify)="eventCalled($event)"></app-child-widget> <div>

Componente principal

export class ParentComponent implements OnInit { returnedItems: Array<any> = []; allItems: Array<any> = []; constructor( ) { } ngOnInit() { this.allItems = // load from server... } eventCalled(items: Array<any>): void { this.returnedItems = items; } }

Componente infantil

@Component({ selector: ''app-child-widget'', templateUrl: ''child.component.html'', styleUrls: [''./child.component.css''] }) export class ChildComponent implements OnInit { @Output() notify: EventEmitter<any> = new EventEmitter(); @Input() private allItems: Array<any>; constructor() { } ngOnInit() { doSomething(); } doSomething() { this.notify.emit(allItems); } }


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

Porque

Su problema es muy similar a este, pero en lugar de actualizar la propiedad principal a través de un servicio, lo está actualizando a través de la transmisión de eventos sincrónicos. Aquí está la cita de la respuesta vinculada :

Durante el ciclo de digestión, Angular realiza ciertas operaciones en directivas infantiles. Una de estas operaciones es actualizar las entradas y llamar al enganche del ciclo de vida de ngOnInit en directivas / componentes secundarios. Lo importante es que estas operaciones se realicen en estricto orden:

  • Actualizar entradas
  • Llama ngOnInit

Entonces, en su caso, Angular actualizó el enlace de entrada allItems en el componente secundario, luego se llamó onInit en el componente secundario, lo que provocó una actualización de allItems del componente principal. Ahora tienes inconsistencias en los datos. El componente padre tiene un valor, mientras que el hijo otro. Si Angular continúa sincronizando los cambios, obtendrás un bucle infinito. Es por eso que durante el siguiente ciclo de detección de cambio, Angular detectó que se cambiaron todos los artículos y se produjo un error.

Solución

Parece que este es un error de diseño de la aplicación, ya que está actualizando los details de los componentes principal y secundario. Si no lo es, entonces puede resolver el problema emitiendo el evento de forma asíncrona de la siguiente manera:

export class ChildComponent implements OnInit { @Output() notify: EventEmitter<any> = new EventEmitter(true); ^^^^^^-------------

Pero hay que tener mucho cuidado. Si usa cualquier otro gancho como ngAfterViewChecked que se está llamando en cada ciclo de resumen, ¡terminará en dependencia cíclica!


En mi caso, estaba cambiando el estado de mis datos (esta respuesta requiere que lea la explicación del ciclo de resumen de AngularInDepth.com). Dentro del nivel html, todo lo que tenía que hacer era cambiar la forma en que manejaba los datos. :

<div>{{event.subjects.pop()}}</div>

dentro

<div>{{event.subjects[0]}}</div>

Resumen: en lugar de hacer estallar, que devuelve y elimina el último elemento de una matriz, cambiando así el estado de mis datos, utilicé la forma normal de obtener mis datos sin cambiar su estado, lo que impide la excepción.