Angular Reactive Form Validation : Expression has changed after it was checked

Viewed 916

I have a Reactive Form with a custom FormControl. The first time the screen is loaded, although the custom control is a required field, my Form is valid.

If I trigger any change, for instance by ticking a checkbox in my form, then the form is marked has invalid until I fill a value to my custom control.

If I reset the value of my custom control, then the form is marked as invalid again and it just works as expected from then on.

So the problem occurs ONLY when the form is loaded the first time on screen.

I've noticed the following exception in the console:

enter image description here

This error is triggered by the validator of my custom control because if I comment it, the error goes away.

Below is the code of my custom control. Am I doing something wrong ?

import { Component, forwardRef, Input, OnInit, Output } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
  selector: 'app-multiple-checkbox',
  templateUrl: './multiple-checkbox.component.html',
  styleUrls: ['./multiple-checkbox.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => MultipleCheckboxComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => MultipleCheckboxComponent),
      multi: true
    }]
})
export class MultipleCheckboxComponent implements OnInit, ControlValueAccessor {

  value = [];
  disabled = false;

  @Input() public options: { key, value }[];

  onTouched: any;
  onChange: any;


  constructor() { }

  validate({ value }: FormControl) {
   
    const isNotValid = this.value.length === 0;
    return isNotValid && {
      invalid: true
    };
  }

  ngOnInit(): void { }

  evaluate(event, key): void {
    if (event.target.checked) {
      this.value.push(key);
    } else {
      this.value.splice(this.value.findIndex(i => key === i), 1);
    }

    this.onChange(this.value);
    this.onTouched();
  }

  isChecked(key): boolean {
    return this.value.findIndex(e => e === key) > -1;
  }

  writeValue(value: any): void {
    this.value = value;
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

}

[EDIT] Adding how the custom control is added to the screen in its parent form:

<div [formGroup]="form">
 <div *ngIf="control.visible">

    <div [ngSwitch]="control.controlType">

        <div *ngSwitchCase="'checkbox'">
            <label [attr.for]="control.key">{{control.label}}</label>

            <input [formControlName]="control.key" [id]="control.key" type="checkbox"
                (change)="evaluate(control.key)">
        </div>

<!--  THIS IS MY CUSTOM CONTROL -->

        <app-multiple-checkbox *ngSwitchCase="'multipleCheckbox'" [formControlName]="control.key"
            [options]="control.options" (change)="evaluate(control.key)">
        </app-multiple-checkbox>
    </div>
</div>

[EDIT]

Following comment below, the error is triggered by this line in my form :

<button type="submit" color="accent" (click)="save()" 
 [disabled]="catalogForm.invalid">Submit</button>
1 Answers

Expression has changed after it was checked

The above error appears only in development mode. In production this error is not there.

To remove this error in development mode

import ChangeDetectorRef from  @angular/core

constructor(private changeDecRef:ChangeDetectorRef) {}

then

    if (event.target.checked) {
      this.value.push(key);
    } else {
      this.value.splice(this.value.findIndex(i => key === i), 1);
    }

    this.onChange(this.value);
    this.onTouched();

    /// ADD THIS FUNCTION ///
    this.changeDecRef.detectChanges();
  }```
Related