How can I validate an input of type="number"
to only be valid if the value is numeric or null using only Reactive Forms (no directives)?
Only numbers [0-9]
and . are allowed, no "e" or any other characters.
What I've tried so far:
Template:
<form [formGroup]="form" novalidate>
<input type="number" formControlName="number" id="number">
</form>
Component:
export class App {
form: FormGroup = new FormGroup({});
constructor(
private fb: FormBuilder,
) {
this.form = fb.group({
number: ['', [CustomValidator.numeric]]
})
}
}
CustomValidator:
export class CustomValidator{
// Number only validation
static numeric(control: AbstractControl) {
let val = control.value;
if (val === null || val === '') return null;
if (!val.toString().match(/^[0-9]+(\.?[0-9]+)?$/)) return { 'invalidNumber': true };
return null;
}
}
Plunker
The problem is when a user enters something that is not a number ("123e" or "abc") the FormControl's value becomes null
, keep in mind I don't want the field to be required so if the field really is empty null
value should be valid.
Cross browser support is also important (Chrome's number input fields do not allow the user to input letters - except "e", but FireFox and Safari do).
Sometimes it is just easier to try something simple like this.
Hope this helps.
updated as per comment, You need to to call the validator like this
The bind(this) is necessary if you are putting the validator in the component which is how I do it.
The easiest way would be to use a library like this one and specifically you want
noStrings
to be trueI had a similar problem, too: I wanted numbers and null on an input field that is not required. Worked through a number of different variations. I finally settled on this one, which seems to do the trick. You place a Directive,
ntvFormValidity
, on any form control that has native invalidity and that doesn't swizzle that invalid state into ng-invalid.Sample use:
<input type="number" formControlName="num" placeholder="0" ntvFormValidity>
Directive definition:
The challenge was to get the ElementRef from the FormControl so that I could examine it. I know there's @ViewChild, but I didn't want to have to annotate each numeric input field with an ID and pass it to something else. So, I built a Directive which can ask for the ElementRef.
On Safari, for the HTML example above, Angular marks the form control invalid on inputs like "abc".
I think if I were to do this over, I'd probably build my own CVA for numeric input fields as that would provide even more control and make for a simple html.
Something like this:
<my-input-number formControlName="num" placeholder="0">
PS: If there's a better way to grab the FormControl for the directive, I'm guessing with Dependency Injection and
providers
on the declaration, please let me know so I can update my Directive (and this answer).In HTML file you can add ngIf for you pattern like this
In .ts file you can add the Validators pattern - "^[0-9]*$"
You need to use regular expressions in your custom validator. For example, here's the code that allows only 9 digits in the input fields:
Take a look at a sample app here:
https://github.com/Farata/angular2typescript/tree/master/Angular4/form-samples/src/app/reactive-validator