ExpressionChangedAfterItHasBeenCheckedError: The expression was changed after checking it. Previous value: 'undefined'

I know that many of the same questions have already been posted in the stack overflow, and I tried different solutions to avoid a runtime error, but none of them work for me.

Error

Component and HTML

export class TestComponent implements OnInit, AfterContentChecked { @Input() DataContext: any; @Input() Position: any; sampleViewModel: ISampleViewModel = { DataContext: : null, Position: null }; constructor(private validationService: IValidationService, private modalService: NgbModal, private cdRef: ChangeDetectorRef) { } ngOnInit() { } ngAfterContentChecked() { debugger; this.sampleViewModel.DataContext = this.DataContext; this.sampleViewModel.Position = this.Position; } <div class="container-fluid sample-wrapper text-center" [ngClass]="sampleViewModel.DataContext?.Style?.CustomCssClass +' samplewidget-'+ sampleViewModel.Position?.Columns + 'x' + sampleViewModel.Position?.Rows"> //some other html here </div> 

Please note: this component is loaded dynamically using DynamicComponentLoader

After troubleshooting, I found a couple of problems

First of all, this child component is loaded dynamically using DynamicComponentResolver and passing input values ​​as shown below

  ngAfterViewInit() { this.renderWidgetInsideWidgetContainer(); } renderWidgetInsideWidgetContainer() { let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName); let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component); let viewContainerRef = this.widgetHost.viewContainerRef; viewContainerRef.clear(); let componentRef = viewContainerRef.createComponent(componentFactory); debugger; (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext; (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position; } 

Even if I change the html of my child component as shown below, I get the same error. Just add the angular ngclass attribute

 <div class="container-fluid ds-iconwidget-wrapper text-center" [ngClass]="Sample"> </div> 

My database and everything is working fine. Do I need to do something with the parent component? I already tried all life cycle events in the child component

+43
source share
8 answers

ngAfterContentChecked life cycle ngAfterContentChecked starts when binding updates for child components / directives are already completed. But you are updating the property, which is used as an input binding for the ngClass directive. This is problem. When Angular starts the validation phase, it detects a pending property update and throws an error.

To better understand the error, read these two articles:

Think about why you need to change the property in the ngAfterViewInit life cycle of the ngAfterViewInit . Any other life cycle that starts before ngAfterViewInit/Checked will work, for example, ngOnInit or ngDoCheck or ngAfterContentChecked .

Thus, to fix this, move renderWidgetInsideWidgetContainer to ngOnInit() life cycle.

+23
source

you must tell angular that you updated the content after ngAfterContentChecked you can import ChangeDetectorRef from @angular/core and call detectChanges

 import {ChangeDetectorRef } from '@angular/core'; constructor( private cdref: ChangeDetectorRef ) {} ngAfterContentChecked() { this.sampleViewModel.DataContext = this.DataContext; this.sampleViewModel.Position = this.Position; this.cdref.detectChanges(); } 
+37
source

I had the same problem when I tried to do something the same as you did, and I fixed it with something similar to Richie Fredixon's answer.

When you run createComponent (), it is created with undefined input variables. Then after that, when you assign data to these input variables, it changes things and causes this error in your child template (in my case it was because I used the input in ngIf, which changed when I assigned the input data).

The only way to avoid this in this particular case is to force detection of changes after assigning the data, however I did not do this in ngAfterContentChecked ().

Your sample code is a bit complicated, but if my solution works for you, it will be something like this (in the parent component):

 export class ParentComponent implements AfterViewInit { // I'm assuming you have a WidgetDirective. @ViewChild(WidgetDirective) widgetHost: WidgetDirective; constructor( private componentFactoryResolver: ComponentFactoryResolver, private changeDetector: ChangeDetectorRef ) {} ngAfterViewInit() { renderWidgetInsideWidgetContainer(); } renderWidgetInsideWidgetContainer() { let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName); let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component); let viewContainerRef = this.widgetHost.viewContainerRef; viewContainerRef.clear(); let componentRef = viewContainerRef.createComponent(componentFactory); debugger; // This <IDataBind> type you are using here needs to be changed to be the component // type you used for the call to resolveComponentFactory() above (if it isn't already). // It tells it that this component instance if of that type and then it knows // that WidgetDataContext and WidgetPosition are @Inputs for it. (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext; (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position; this.changeDetector.detectChanges(); } } 

Mine is almost the same as the one, except that I use @ViewChildren instead of @ViewChild, since I have several host elements.

+1
source

If you use <ng-content> with *ngIf you are sure to *ngIf in this loop.

The only way I found is to change *ngIf for display:none functionality

+1
source

Two solutions:

  1. Make sure you have some binding variables, then move this code to settimeout ({}, 0);
  2. Move related code to ngAfterViewInit method
+1
source
 setTimeout(() => { // your code here }, 0); 

I wrapped my code in setTimeout and it worked

+1
source

I do not understand why this happened

0
source
  ngAfterViewInit() { setTimeout(() => { this.renderWidgetInsideWidgetContainer(); }, 0); } 

This is a good solution to solve this problem.

0
source

Source: https://habr.com/ru/post/1270498/


All Articles