Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'

34,602

Solution 1

I faced the same issue and I fixed it by using AfterViewChecked and ChangeDetectorRef:

import { AfterViewChecked, ChangeDetectorRef } from '@angular/core'

export class ClassName implements AfterViewChecked {
  constructor(private readonly changeDetectorRef: ChangeDetectorRef) {}

  ngAfterViewChecked(): void {
    this.changeDetectorRef.detectChanges();
  }
}

Solution 2

Change the ChangeDetectionStrategy on the parent component to : changeDetection: ChangeDetectionStrategy.OnPush. Simple and clean.

Edit This actually not a solution, it will just hide the error. Please take a look at the github link posted in the comments.

Solution 3

I am facing same issue I know it's not a way but the only solution I've found is setTimeout

ngAfterViewInit(): void {
 setTimeout(() => {
    this.form.patchValue({
    name: 'xyz',
    email: '[email protected]',
    phone: '0987654321'
   })
  }, );
}

Solution 4

I don't know if it's the right thing to do but i solved it by changing:

<div *ngIf="db.checked" formGroupName="_monitorDB">

to

<div [hidden]="!db.checked" formGroupName="_monitorDB">

Solution 5

I tend to never use setTimeout as I think it's a bad practice, but I have lately found that when creating custom components and interacting with the DOM, this is sometimes necessary.

However, when I've used it, it has been with a delay of 0ms. Just wrapping the action in a setTimeout with no delay will be enough to move the action to the end of the execution queue, which will solve numerous issues with the interaction between Angular and the DOM.

TL,DR

setTimeout(() => actionThatCausesTheError(), 0);
Share:
34,602
Livnat Menashe
Author by

Livnat Menashe

Updated on July 14, 2021

Comments

  • Livnat Menashe
    Livnat Menashe almost 3 years

    I have angular reactive form in parent component and sections inside childrens component.

    Inside the child component I have a checkbox - when its checked - more fields open and I want them all to be required.

    I am using setValidators but I'm getting error

    ParentFormComponent.html:3 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'. at viewDebugError (core.js:7601) at expressionChangedAfterItHasBeenCheckedError (core.js:7589) at checkBindingNoChanges (core.js:7691) at checkNoChangesNodeInline (core.js:10560) at checkNoChangesNode (core.js:10541) at debugCheckNoChangesNode (core.js:11144) at debugCheckRenderNodeFn (core.js:11098) at Object.eval [as updateRenderer] (ParentFormComponent.html:3) at Object.debugUpdateRenderer [as updateRenderer] (core.js:11087) at checkNoChangesView (core.js:10442)

    ParentFormComponent.html:3 ERROR CONTEXT DebugContext_ {view: Object, nodeIndex: 2, nodeDef: Object, elDef: Object, elView: Object}

    this is the line of ParentFormComponent.html:3

     <form [formGroup]="parentForm" (ngSubmit)="submitForm()">
    

    Here is my code:

    <label class="container">DB
        <input #db type="checkbox" name="db" (change)="checkValue(db.name, db.checked)"> 
        <span class="checkmark"></span>
    </label>
    
    <div *ngIf="db.checked" formGroupName="_monitorDB">
        <mat-form-field>
            <input matInput placeholder="Server name" formControlName="ServerName">
        </mat-form-field>
    
        <mat-form-field>
            <input matInput placeholder="DataBase name" formControlName="DbName">
        </mat-form-field>
    
        <mat-form-field>
            <input matInput placeholder="table name" formControlName="DB_tableName">
        </mat-form-field>
    
        <mat-form-field>
            <input matInput placeholder="port" formControlName="DbPort">
        </mat-form-field>
    
        <mat-form-field>
            <input matInput placeholder="Query" formControlName="Query">
        </mat-form-field>
    
        <mat-form-field>
            <input matInput placeholder="Permissions" formControlName="Premissions">
        </mat-form-field>
    
    </div>
    

    and in the ts file:

    checkValue(name:string, event: any){
        if (event == true){
          this.test.push(name);
          if (name =="db"){
             this.childForm.get('_monitorDB').get('ServerName').setValidators([Validators.required]);
             this.childForm.get('_monitorDB').get('DbName').setValidators([Validators.required]);
             this.childForm.get('_monitorDB').get('DB_tableName').setValidators([Validators.required]);
             this.childForm.get('_monitorDB').get('DbPort').setValidators([Validators.required]);
             this.childForm.get('_monitorDB').get('Query').setValidators([Validators.required]);
             this.childForm.get('_monitorDB').get('Premissions').setValidators([Validators.required]);
    
          }
    
        }
    
        else{
          const index: number = this.test.indexOf(name);
          if (index !== -1) {
              this.test.splice(index, 1);
              if (name =="db"){
                this.childForm.get('_monitorDB').get('ServerName').clearValidators();
                this.childForm.get('_monitorDB').get('ServerName').updateValueAndValidity();
                this.childForm.get('_monitorDB').get('DbName').clearValidators();
                this.childForm.get('_monitorDB').get('DbName').updateValueAndValidity();
                this.childForm.get('_monitorDB').get('DB_tableName').clearValidators();
                this.childForm.get('_monitorDB').get('DB_tableName').updateValueAndValidity();
                this.childForm.get('_monitorDB').get('DbPort').clearValidators();
                this.childForm.get('_monitorDB').get('DbPort').updateValueAndValidity();
                this.childForm.get('_monitorDB').get('Query').clearValidators();
                this.childForm.get('_monitorDB').get('Query').updateValueAndValidity();
                this.childForm.get('_monitorDB').get('Premissions').clearValidators();
                this.childForm.get('_monitorDB').get('Premissions').updateValueAndValidity();
              }
          }      
        }
    
         this.checkboxArr.emit(this.test);
     }