4

I have a custom validator with parameters set on a FormGroup and it is running once when initialized but is not being triggered on any of the control changes. Removing the parameters runs the validator on each control change but obviously doesn't work without the parameters. Any suggestions to getting this to run on each control change? I've tried to watch the controls and use updateValueAndValidity() and still have no luck.

const customValidator = (options: { min: number, max: number }): ValidatorFn => {
  console.log(options);
  return (control: AbstractControl): { [key: string]: boolean } | null => {
    // logic returning null or { 'not-enough': true }
  }
}
    
this.form = new FormGroup({
  controlOne: new FormControl(null),
  controlTwo: new FormControl(null)
}, { validators: [customValidator({min: 5, max: 10})]});

Found Solution

Thanks to comments below and other answers I realized that my console logs outside for the return function of the validator only run once. Moving that and any other additional logic inside of the return function, runs as expected. Ultimately my solution was just moving some code down a line...

const customValidator = (options: { min: number, max: number }): ValidatorFn => {
  // No code up here will run on each control being validated
  return (control: AbstractControl): { [key: string]: boolean } | null => {
    // code down here will run on every control change
    // logic returning null or { 'not-enough': true }
  }
}
    
this.form = new FormGroup({
  controlOne: new FormControl(null),
  controlTwo: new FormControl(null)
}, { validators: [customValidator({min: 5, max: 10})]});
4
  • any errors in your console? Commented Apr 7, 2019 at 4:22
  • There are no errors in the console.The issue is the validator only runs once when the form instance is created. I'm looking to run the validator anytime a control within the form group changes. Commented Apr 7, 2019 at 20:09
  • 1
    Seems to work just fine in this demo: stackblitz.com/edit/angular-form-custom-validation-gcbyvt. Are you doing anything different? Commented Apr 7, 2019 at 20:26
  • Yes thank you, I found the issue. I had some object de-structuring of the options params above return along with my console logs. I see the factory runs once but the validator is everything in the return function. I moved my logic inside of the return function and all is working now. Commented Apr 7, 2019 at 22:38

1 Answer 1

2

You should have an error in the console because you're not returning anything in your ValidatorFn:

ERROR in src/app/app.component.ts(13,44): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.

Template

  1. Be sure to bind the FormGroup to the form
  2. Be sure to bind every FormControl

Code

<div style="text-align:center">
  <form [formGroup]="form">
      <input type="text" formControlName="controlOne">
      <input type="submit">
  </form>
</div>

Module

  1. be sure to import the ReactiveFormsModule

Code

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Controller

  1. import FormControl, FormGroup, AbstractControl, ValidatorFn
  2. return from the ValidatorFn

Code

import { Component } from '@angular/core';
import { FormControl, FormGroup, AbstractControl, ValidatorFn } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
    customValidator = (options: { min: number, max: number }): ValidatorFn => {
        return (control: AbstractControl): { [key: string]: boolean } | null => {
            console.log(options.min, options.max);
            return {};//RETURN ERRORS HERE
        }
    }

    form = new FormGroup({
        controlOne: new FormControl(null)
    }, { validators: [this.customValidator({min: 5, max: 10})]});
}
Sign up to request clarification or add additional context in comments.

9 Comments

I'm not sure I understand your question then. This validator runs whenever the controls in the FormGroup change. Is that not the intended functionality?
The ValidatorFnFactory will of course only run once if that is what you're referring to.
Any suggestions to getting this to run on each control change? Maybe I don't understand what this is. Are talking about the custom validator factory or the validator returned from it? The latter works already, so you must be referring to the former... Is that correct?
That is correct. The customValidator function is actually a factory function. When you called it with customValidator({min: 5, max: 10}), it returned the ValidatorFn. Whatever is in the returned ValidatorFn will execute on each FormControl change.
Great thank you, I've edited the question to also include these notes for future viewers.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.