0
votes

I have an Angular application that uses a reactive form. One of my input fields is of type number and I use the step directive to determine valid values as following:

steps = 0.25
myForm: FormGroup

constructor(private fb: FormBuilder) {
  myForm = this.fb.group({
      numberControl: new FormControl(null)
  });

<input type="number" step="{{steps}}" formControlName="numberControl">

The step property can be changed based on other input of the application but for simplicity I have not included that in this example. My problem here is that the form is always valid. The step works when I use the up and down arrow keys of the input field but I could enter any value manually and it would still be valid.

Stackblitz example: https://stackblitz.com/edit/angular-rhyeeb

Any help is appreciated!

2
You mean to say that the number input field should only be controlled only via the stepper arrows and not through keyboard. Am I right? - Shravan
No I want the form to be invalid if I manually enter a value that is not a valid step. So in my example something like 0.26 - RDNotorious
You should create then a custom validator for your input field. You can find tons of articles in google regarding that. - standby954

2 Answers

1
votes

By default, numner input fields don't validate if the number entered manually by user is divisible by the step counter. If you want a feature like that, then you have to implement a custom validator which is pretty easy.

All you have to do is create a function to validate the number input field and then include the newly created validator in the Validators array of the FormControl object. Your validator function should either return a null value when your custom validation test passes or return a map of key-value pairs detailing the reason for failure.

Find the below code snippet demonstrating the use of custom validator to check if the current input value is a valid step or not.

export class AppComponent implements OnInit {
  steps = 0.25;
  myForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.fb.group({
      numberControl: new FormControl(null, [
        Validators.required,
        this.validateNumField.bind(this)
      ])
    });

    this.myForm.valueChanges.subscribe(_ => {
      if (!this.myForm.valid) {
        console.log('err msg -> ', this.myForm.get("numberControl").errors);
      }
    });
  }

  validateNumField(control: AbstractControl): { [key: string]: any } {
    let rem = control.value && Number.parseFloat(control.value) % this.steps;
    // console.log(rem);

    /**
    * If the remainder is not 0 then user has entered an invalid value
    */
    return rem ? { error: "Not a valid step" } : null;
  }
}

Now your submit button will not work if it is not a valid step. You can go ahead and show an error message on the template if you want using this.myForm.get("numberControl").errors property.

You can find an working example in stackblitz here. For more on custom validation, visit Angular docs.

Edit: Updated answer to capture change in steps value.

2
votes

You need to implement a custom validation yourself.

import { AbstractControl } from '@angular/forms';

function stepsValidator(control: AbstractControl) {
    if (!control.value || control.value % this.steps !== 0) {
        return { error: true };
    }
    return null;
}

And then in your formControl declaration

myForm = this.fb.group({
    numberControl: new FormControl(null, this.stepsValidator.bind(this))
});

You must use the bind function since custom validators has a different scope than the component their in.