I have built a table component ez-table that allows you to pass in a template to the column to render the current item. This is all working great but for some reason when I have a form field that has a validation attribute that relies on the data in another form field I get the error
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'.
if changing the value of a field makes another field invalid.
In the following example if one field on the row has a value the other becomes required.
<form #form="ngForm">
<ez-table [data]="rows">
<ez-column heading="Val 1" property="val1">
<ng-template let-row let-i="index">
<input [name]="'val1_' + i" [(ngModel)]="row.val1" [required]="!!row.val2">
</ng-template>
</ez-column>
<ez-column heading="Val 2" property="val2">
<ng-template let-row let-i="index">
<input [name]="'val2_' + i" [(ngModel)]="row.val2" [required]="!!row.val1">
</ng-template>
</ez-column>
</ez-table>
Valid: {{ form.valid | json }}
</form>
I cannot understand why this happens in my table component when I can do exactly the same thing without the component and don't get the error.
This table is doing exactly the same thing but entering data into one field doesn't trigger the error but does update the validity of the form.
<form #form2="ngForm">
<table>
<thead>
<tr><th>Val 1</th><th>Val 2</th></tr>
</thead>
<tbody>
<tr *ngFor="let row of rows2;index as i">
<td>
<ng-container *ngTemplateOutlet="editTemplate1;context:{ $implicit: row, index: i }">
</ng-container>
</td>
<td>
<ng-container *ngTemplateOutlet="editTemplate2;context:{ $implicit: row, index: i }">
</ng-container>
</td>
</tr>
</tbody>
</table>
Valid: {{ form2.valid | json }}
<ng-template #editTemplate1 let-row let-i="index">
<input [name]="'val1_' + i" [(ngModel)]="row.val1" [required]="!!row.val2">
</ng-template>
<ng-template #editTemplate2 let-row let-i="index">
<input [name]="'val2_' + i" [(ngModel)]="row.val2" [required]="!!row.val1">
</ng-template>
</form>
Here is a StackBlitz
https://stackblitz.com/edit/angular-k58bmn?file=src/app/app.component.html
and the source code to the table component is at
Add detectChanges in your component.
Fix: https://stackblitz.com/edit/angular-xsgyqj
import { Component, ChangeDetectorRef } from "@angular/core";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
rows = Array.from({ length: 5 }, () => ({}));
rows2 = Array.from({ length: 5 }, () => ({}));
constructor(private changeDetection: ChangeDetectorRef) {}
ngAfterContentChecked() {
this.changeDetection.detectChanges();
}
}