I have a parent Component which updates a every second its array myValue
. In a child component I want to create a chart which uses this array as data and updates also every time the parent updates.
When I run this application I get this error:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'hidden: true'. Current value: 'hidden: false'.
Here is my parent component:
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements AfterContentInit, OnDestroy {
private myValues: MyValue[];
private alive: boolean;
constructor(private valueService: ValueService) {
this.alive = true;
this.myValues = [];
}
ngAfterContentInit(): void {
TimerObservable.create(0, 1000)
.takeWhile(() => this.alive)
.subscribe(() => {
this.valueService.doSmth().subscribe(
value => {
this.myValues.push(value);
}
);
});
}
...
}
The parent template looks like this:
<ul>
<li *ngFor="let value of myValues">
<p>{{value.name}}</p>
</li>
</ul>
<app-value-chart [chartData] = myValues></app-value-chart>
And here is my child component:
@Component({
selector: 'app-value-chart',
templateUrl: './value-chart.component.html',
styleUrls: ['./value-chart.component.scss']
)}
export class ValueChartComponent implements AfterViewInit {
@Input() chartData: MyValue[];
chart: any;
ngAfterViewInit(): void {
this.createChart(); // creates chart with ChartJS
const tmp: number[] = [];
for (let i = 0; i < this.chartData.length; i++) {
tmp.push(this.chartData[i].x);
}
this.chart.data.datasets[0].data = tmp;
this.chart.update(0);
}
...
}
Child template:
<canvas id="canvas" responsive>{{ chart }}</canvas>
How can I solve my problem?
I use Angular 6.
What is ExpressionChangedAfterItHasBeenCheckedError? The ExpressionChangedAfterItHasBeenCheckedError error is thrown up when the binding expression changes after being checked by Angular during the change detection cycle.
This often means refactoring to use the correct component lifecycle hook for your use case. If the issue exists within ngAfterViewInit , the recommended solution is to use a constructor or ngOnInit to set initial values, or use ngAfterContentInit for other value bindings.
You can find detailed explanations about that exception in this article. One technique to eliminate the exception is to force change detection with ChangeDetectorRef.detectChanges
:
export class ValueChartComponent implements AfterViewInit {
constructor(private cd: ChangeDetectorRef) { }
ngAfterViewInit(): void {
...
this.cd.detectChanges();
}
...
}
An alternative technique is to run the handler code asynchronously with setTimeout
:
export class ValueChartComponent implements AfterViewInit {
ngAfterViewInit(): void {
setTimeout(() => {
...
});
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With