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.