Home > Software design >  Angular - reactive forms with multipe checkboxes as a array touched validator issue
Angular - reactive forms with multipe checkboxes as a array touched validator issue

Time:12-07

I'm trying to create a form where the user can select multiple colors and then submit the form.

Code

  colors: Array<any> = [
{ description: 'White', value: 'White' },
{ description: 'Black', value: 'Black' },
{ description: 'Blue', value: 'Blue' },
{ description: 'Green', value: 'Green' },
{ description: 'Yellow', value: 'Yellow' },
{ description: 'Red', value: 'Red' },
  ];

  constructor(
    private formBuilder: FormBuilder
  ) {
    this.fromInit();
  }

  ngOnInit(): void {}

  fromInit() {
    this.form = this.formBuilder.group({
      colors: this.formBuilder.array([], [Validators.required]),
    });
  }

  onCheckChange(event) {
    const formArray: FormArray = this.form.get(
      'colors'
    ) as FormArray;

    if (event.target.checked) {
      formArray.push(new FormControl(event.target.value));
    } else {
      let i: number = 0;

      formArray.controls.forEach((ctrl: FormControl) => {
        if (ctrl.value == event.target.value) {
          formArray.removeAt(i);
          return;
        }
        i  ;
      });
    }
  }

  invalidColorsMessage() {
    if (this.form.controls['colors'].errors?.required)
      return "You must choose a color";
  }

HTML

      <div 
       *ngIf="this.form.controls['colors'].touched &&
        this.form.controls['colors'].invalid">
    {{invalidColorsMessage()}}</div>

The issue that I encountered that property 'touched' of the this.form.controls['colors'] is always false. no matter if I select a few checkboxes or don't select anything the value of 'touched' is always false.

I don't really understand why it's always false when I check and uncheck the checkboxes.

the expected result is to show an error message whenever someone checks and unchecks the boxes and leaving all empty.

I would like to know why the value of this.form.controls['colors'].touched is always false no matter what. So I tried to search for the answer online but unfortunately with no success in finding the solution.

Here is a demo of the checkboxes : https://stackblitz.com/edit/angular-ivy-s8rc6j?file=src/app/app.component.html

thanks

CodePudding user response:

Update stackblitz:

  1. set isTouched = false; to global variable

  2. check *ngIf=="isTouched && this.form.controls['colors'].invalid"

  3. added this.isTouched = true; in onCheckChange(event) {}

This is Github closed issue

The touched property reflects whether a form control has been blurred, not changed.

The form touched would never return true because the blurred event only happened at input element.

You could change your code from:

<div class="invalid-input-message" *ngIf="this.form.controls['colors'].touched && this.form.controls['colors'].invalid">

To: (remove this.form.controls['colors'].touched condition)

<div class="invalid-input-message" *ngIf="this.form.controls['colors'].invalid">

CodePudding user response:

You could use a custom validator, good thing, it is reusable! We attach it to the formarray and check the length of the array, if length is at least 1, we markAllAsTocuhed, therefore you can keep your touched in the template. So the function would look like:

export function validate(ctrls: FormArray): ValidationErrors | null {
  if (ctrls.value.length) {
    ctrls.markAllAsTouched();
    return null;
  }
  return { notValid: true };
}

You can put this in a service or somewhere where other components can import it if you want it to be reusable.

OK, so now let's attach this to the formarray:

fromInit() {
  this.form = this.formBuilder.group({
    colors: this.formBuilder.array([], { validators: validate }),
  });
}

And then in the template you check if error exist and if it's touched:

<div *ngIf="form.get('colors').hasError('notValid') && form.get('colors').touched">
  Please choose at least one
</div>

Your forked STACKBLITZ

  • Related