Angular - assign custom validator to a FormGroup

66,239

Solution 1

I've created a stackblitz take a look.

In the component.ts file

import { Component } from '@angular/core';
import {FormBuilder,FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms'

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  myForm: FormGroup;
  defaultValue = 20;

constructor(private formBuilder: FormBuilder) {
  this.myForm = this.formBuilder.group({
        myControl1: this.defaultValue,
        myControl2: this.defaultValue
      });
      debugger
      this.myForm.setValidators(this.comparisonValidator())
}

 public comparisonValidator() : ValidatorFn{
       return (group: FormGroup): ValidationErrors => {
          const control1 = group.controls['myControl1'];
          const control2 = group.controls['myControl2'];
          if (control1.value !== control2.value) {
             control2.setErrors({notEquivalent: true});
          } else {
             control2.setErrors(null);
          }
          return;
    };
 }
}

In the component.html file

<div>
  <form [formGroup]="myForm">
    <input formControlName="myControl1" type="number">
    <input formControlName="myControl2"  type="number">
    <br>Errors: {{myForm.get('myControl2').errors | json}}
  </form>
</div>

Solution 2

For setting any validators (predefined or customs) after the instantiating the formGroup, you will need to use the setValiators() method of FormGroup. For Ex:

  let myFormGroup = this.    _fb.group({
      control1: new FormControl('1', [])
    });
  myFormGroup.setValidators(this.customValidators());
  customValidators(): ValidatorFn {
   let myFun = (cc: FormGroup): ValidationErrors => {
     if(cc.valid) return null;
     else return {something: 'someError'};
   };
   return myFun;
  }

Solution 3

Thanks to @Anuradha Gunasekara - his answer is the most correct and complete solution. A 'quick fix' for my error was just to add a return type of any on the validator. I can still assign the custom validator to the FormGroup, and the FormGroup will be passed implicitly as the argument to my custom validator. This code will work:

let myForm : FormGroup;

myForm = this.formBuilder.group({
           myControl1: defaultValue,
           myControl2: defaultValue
         })

this.myForm.validator = this.comparisonValidator;

comparisonValidator(g: FormGroup) : any {
      if (g.get('myControl1').value > g.get('myControl2'.value)) {
        g.controls['myControl1'].setErrors({ 'value2GreaterThanValue1': true });
      }
}

Solution 4

Remove all form control white space form Form Group

custom validator :

    export function formGroupRemoveWhitespaceValidator(form: FormGroup): ValidationErrors | null {
    const keys: string[] = Object.keys(form.controls);

    if (keys.length) {
        keys.forEach(k => {
            let control = form.controls[k];
            
            if (control && control.value && !control.value.replace(/\s/g, '').length) {
                control.setValue('');
            }
        });
    }

    return null;
}

component :

let myForm : FormGroup;

myForm = this.formBuilder.group({
           myControl1: defaultValue,
           myControl2: defaultValue
         }, { validators: formGroupRemoveWhitespaceValidator });
Share:
66,239

Related videos on Youtube

Chris Halcrow
Author by

Chris Halcrow

I'm a software engineer

Updated on April 05, 2022

Comments

  • Chris Halcrow
    Chris Halcrow about 2 years

    I need to assign a custom validator to a FormGroup. I can do this at the time the FormGroup is created like this:

    let myForm : FormGroup;
    
    myForm = this.formBuilder.group({
            myControl1: defaultValue,
            myControl2: defaultValue
          }, { validator: this.comparisonValidator })
    
    comparisonValidator(g: FormGroup) {
          if (g.get('myControl1').value > g.get('myControl2'.value)) {
            g.controls['myControl1'].setErrors({ 'value2GreaterThanValue1': true });
            return;
          }
    }
    

    I have a situation though where I need to add the custom validator after I've instantiated the FormGroup, so I'm trying to do this after instantiating myForm, instead of adding the validator when the form is instantiated:

    let myForm : FormGroup;
    
    myForm = this.formBuilder.group({
            myControl1: defaultValue,
            myControl2: defaultValue
          })
    
    this.myForm.validator = this.comparisonValidator;
    

    This gives me a compiler error:

    Type '(g: FormGroup) => void' is not assignable to type 'ValidatorFn'.
      Type 'void' is not assignable to type 'ValidationErrors'.
    

    How do I assign a validator to my FormGroup so that the formGroup is passed as the argument to my comparisonValidator function?

    Update - I've added a line showing where I'm doing a setErrors in my comparisonValidator, to make it clearer exactly how I'm trying to set a validation error.

    • Keshan Nageswaran
      Keshan Nageswaran almost 6 years
      Adding dynamic validator can be done using setValidators(). but specific for a particular control you can update at a time
    • Crisu83
      Crisu83 almost 4 years
      You get this error because you return void from your comparisonValidator function. Change the function to return null on the last line (not inside the if-statement) and the error should be gone.
  • Chris Halcrow
    Chris Halcrow almost 6 years
    Thanks Anuradha. To clarify what I'm asking, I'm trying to only add the validator after the FormGroup is instantiated, instead of adding it when the form is instantiated. I've edited the question a little to make it clearer.
  • Abhishek Jaiswal
    Abhishek Jaiswal almost 6 years
    Better solution than returning any from validator.
  • Anuradha Gunasekara
    Anuradha Gunasekara almost 6 years
    @ChrisHalcrow I've updated the ansewer take alook. nd You can find a working example in here.