Home > Net >  Custom validator in Angular 13 which forces to check at least one checkbox is not working
Custom validator in Angular 13 which forces to check at least one checkbox is not working

Time:06-10

I'm using Angular 13 and I'm trying to apply a custom validator for some of the fields of a form. Lets say I have:

  • Input 1
  • Input 2
  • Checkbox 1
  • Checkbox 2
  • Checkbox 3

in which I bind an ngForm and a bidirectional ngModel in every field:

<form  #inspectionForm="ngForm">
...
...
...
 <div >
    <input  type="text " [(ngModel)]="inspectionService.checkbox1" name="checkbox1" required />
 </div>
</form>
<button [disabled]="!inspectionForm.form.valid" type="button"  (click)="submit();">Continue</button>

I want to apply the validator to the checkboxes only. It must force the user to check at least one.

The point is to disable a button by using [disabled]="!inspectionForm.form.valid"

This is essencially my code in the form.component.ts:

  @ViewChild('inspectionForm') inspectionForm ?: NgForm;

      atLeastOneValidator(checkboxes: string[]): ValidatorFn{
    return (control: AbstractControl): { [key: string]: boolean } | null => {
      for(let i = 0; i < checkboxes.length; i  ){
        if(this.inspectionForm!.form.get(checkboxes[i])?.value === true) return null;
      }
      return  { 'atLeastOne': true };
    }
  }



  ngOnInit(): void {
    let checkboxes = ['checkbox1', 'checkbox2', 'checkbox3']; // Checkboxes' names

    checkboxes.forEach(item => {
      this.inspectionForm?.form.controls[item].setValidators([this.atLeastOneValidator(checkboxes)]);
      this.inspectionForm?.form.controls[item].updateValueAndValidity({onlySelf: true});
    })

but it doesn't work. I don't know what am I doing wrong or if I skipped something

CodePudding user response:

A multi-control validatino should be made on the form, not on a control.


form = this.formBuilder.group({ /* ... */ }, [atLeastOne(['cb1'])]);

// Some file somewhere

export function atLeastOne(boxNames) {
  return function(form) {
    const boxes = Object.entries(form.controls)
      .filter(([key]) => boxNames.includes(key))
      .map(([, v]) => v);

    const valid = boxes.some(box => !!box.value);

    return valid ? null : { 'atleastone': true };
  }
}

CodePudding user response:

The problem is that "this" has no value into your function atLeastOneValidator

You can use bind(this) (it's javascript bind)

this.inspectionForm?.form.controls[item].setValidators(
     [this.atLeastOneValidator(checkboxes).bind(this)]);

but be sure you get the form inside your function atLeastOneValidator

Or "reach" the form using control.parent

atLeastOneValidator(checkboxes: string[]): ValidatorFn {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
      const parent = control.parent as FormGroup;
      if (!parent)
        return null;
      const success = checkboxes.reduce((a,b)=>a || parent.get(b).value,false);

      return success ? null : { atLeastOne: true };
    };
  }

But be carefull, a FormControl only is checked if valid or not when have a input relationated and the input change or when we call manually to updateValueAndValidity.

So, we can create a function

  updateValueAndValidity(...checkboxes:string[])
  {
    checkboxes.forEach((x) => {
      this.form.get(x).updateValueAndValidity({emitEvent:false})
    });
  }

And call in .html

<form [formGroup]="form">
  <input type="checkbox" formControlName="checkbox1" 
      (change)="updateValueAndValidity('checkbox2','checkbox3')"/>
  <input type="checkbox" formControlName="checkbox2" 
      (change)="updateValueAndValidity('checkbox1','checkbox3')"/>
  <input type="checkbox" formControlName="checkbox3" 
      (change)="updateValueAndValidity('checkbox1','checkbox2')"/>
</form>

Or subscribe to valuesChanges

  • Related