Min / Max Validator in Angular 2 Final
ValidationAngularTypescriptAngular2 FormsValidation Problem Overview
According to thoughtgram.io, the currently supported validators are:
- required
- minlength
- maxlength
- pattern
So, considering the following code (plunkr here):
@Component({
selector: 'my-app',
template: `
<form #formRef="ngForm">
<input type="number" [(ngModel)]="firstValue" name="firstValue" min="0" required/>
<input type="text" [(ngModel)]="secondValue" maxlength="5" name="secondValue" required/>
<button type="submit"> Submit </button>
</form>
FORM: {{formRef.form | json }}
`
})
export class AppComponent {
firstValue = -22;
secondValue = "eyy macarena!";
}
While minlength
is supported, min="0"
is ignored by angular validation:
So, to make the form result in an error when firstValue ngModel < 0, do I need to build a custom validator?
Validation Solutions
Solution 1 - Validation
To apply min/max validation
on a number
you will need to create a Custom Validator
Validators class currently only have a few validators, namely
- required
- requiredTrue
- minlength
- maxlength
- pattern
- nullValidator
- compose
- composeAsync
Validator: Here is toned down version of my number Validator, you can improve it as you like
static number(prms = {}): ValidatorFn {
return (control: FormControl): {[key: string]: any} => {
if(isPresent(Validators.required(control))) {
return null;
}
let val: number = control.value;
if(isNaN(val) || /\D/.test(val.toString())) {
return {"number": true};
} else if(!isNaN(prms.min) && !isNaN(prms.max)) {
return val < prms.min || val > prms.max ? {"number": true} : null;
} else if(!isNaN(prms.min)) {
return val < prms.min ? {"number": true} : null;
} else if(!isNaN(prms.max)) {
return val > prms.max ? {"number": true} : null;
} else {
return null;
}
};
}
Usage:
// check for valid number
var numberControl = new FormControl("", [Validators.required, CustomValidators.number()])
// check for valid number and min value
var numberControl = new FormControl("", CustomValidators.number({min: 0}))
// check for valid number and max value
var numberControl = new FormControl("", CustomValidators.number({max: 20}))
// check for valid number and value range ie: [0-20]
var numberControl = new FormControl("", CustomValidators.number({min: 0, max: 20}))
Solution 2 - Validation
I found a library implementing a lot of custom validators - ng2-validation - that can be used with template-driven forms (attribute directives). Example:
<input type="number" [(ngModel)]="someNumber" name="someNumber" #field="ngModel" [range]="[10, 20]"/>
<p *ngIf="someNumber.errors?.range">Must be in range</p>
Solution 3 - Validation
You can implement your own validation (template driven) easily, by creating a directive that implements the Validator
interface.
import { Directive, Input, forwardRef } from '@angular/core'
import { NG_VALIDATORS, Validator, AbstractControl, Validators } from '@angular/forms'
@Directive({
selector: '[min]',
providers: [{ provide: NG_VALIDATORS, useExisting: MinDirective, multi: true }]
})
export class MinDirective implements Validator {
@Input() min: number;
validate(control: AbstractControl): { [key: string]: any } {
return Validators.min(this.min)(control)
// or you can write your own validation e.g.
// return control.value < this.min ? { min:{ invalid: true, actual: control.value }} : null
}
}
Solution 4 - Validation
> Angular now supports min/max validators by default.
Angular provides the following validators by default. Adding the list here so that new comers can easily get to know what are the current supported default validators and google it further as per their interest.
- min
- max
- required
- requiredTrue
- minLength
- maxLength
- pattern
- nullValidator
- compose
- composeAsync
you will get the complete list Angular validators
How to use min/max validator: From the documentation of Angular -
static min(min: number): ValidatorFn
static max(max: number): ValidatorFn
> min()/max() is a static function that accepts a number parameter and returns > A validator function that returns an error map with the min/max property > if the validation check fails, otherwise null.
use min validator in formControl, (for further info, click here)
const control = new FormControl(9, Validators.min(10));
use max validator in formControl, (for further info, click here)
const control = new FormControl(11, Validators.max(10));
sometimes we need to add validator dynamically. setValidators() is the saviour. you can use it like the following -
const control = new FormControl(10);
control.setValidators([Validators.min(9), Validators.max(11)]);
Solution 5 - Validation
I was looking for the same thing now, used this to solve it.
My code:
this.formBuilder.group({
'feild': [value, [Validators.required, Validators.min(1)]]
});
Solution 6 - Validation
As far as I know, is it implemented now, check https://github.com/angular/angular/blob/master/packages/forms/src/validators.ts
This is the part that implements what you are looking for:
export class Validators {
/**
* Validator that requires controls to have a value greater than a number.
*/
static min(min: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) {
return null; // don't validate empty values to allow optional controls
}
const value = parseFloat(control.value);
// Controls with NaN values after parsing should be treated as not having a
// minimum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-min
return !isNaN(value) && value < min ? {'min': {'min': min, 'actual': control.value}} : null;
};
}
/**
* Validator that requires controls to have a value less than a number.
*/
static max(max: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) {
return null; // don't validate empty values to allow optional controls
}
const value = parseFloat(control.value);
// Controls with NaN values after parsing should be treated as not having a
// maximum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-max
return !isNaN(value) && value > max ? {'max': {'max': max, 'actual': control.value}} : null;
};
}
Solution 7 - Validation
Apparently, Angular had the max/min directives for template driven forms at some point but had to remove them in v4.2.0. You can read about the regression that caused the removal here: https://github.com/angular/angular/issues/17491
For now the only working solution that I know of is to use custom directive as @amd suggested. Here's how to use it with Bootstrap 4.
min-validator.directive.ts
import { Directive, Input } from '@angular/core'
import { NG_VALIDATORS, Validator, AbstractControl, Validators } from '@angular/forms'
@Directive({
selector: '[min]',
providers: [{ provide: NG_VALIDATORS, useExisting: MinDirective, multi: true }]
})
export class MinDirective implements Validator {
@Input() min: number;
validate(control: AbstractControl): { [key: string]: any } {
return Validators.min(this.min)(control)
}
}
And in your template:
<input type="number" [min]="minAge" #age="ngModel" [(ngModel)]="person.age" class="form-control" [ngClass]="{'is-invalid':age.invalid}">
<div *ngIf="age.invalid && (age.dirty || age.touched)" class="invalid-feedback">You need to be older than {{minAge}} to participate</div>
Hope this helps!
Solution 8 - Validation
-
Switch to use reactive forms instead of template forms (they are just better), otherwise step 5 will be slightly different.
-
Create a service NumberValidatorsService and add validator functions:
import { Injectable } from '@angular/core'; import { FormControl, ValidatorFn } from '@angular/forms'; @Injectable() export class NumberValidatorsService { constructor() { } static max(max: number): ValidatorFn { return (control: FormControl): { [key: string]: boolean } | null => { let val: number = control.value; if (control.pristine || control.pristine) { return null; } if (val <= max) { return null; } return { 'max': true }; } } static min(min: number): ValidatorFn { return (control: FormControl): { [key: string]: boolean } | null => { let val: number = control.value; if (control.pristine || control.pristine) { return null; } if (val >= min) { return null; } return { 'min': true }; } } }
-
Import service into module.
-
Add includes statement in component where it is to be used:
import { NumberValidatorsService } from "app/common/number-validators.service";
-
Add validators to form builder:
this.myForm = this.fb.group({ numberInputName: [0, [Validators.required, NumberValidatorsService.max(100), NumberValidatorsService.min(0)]], });
-
In the template, you can display the errors as follows:
<span *ngIf="myForm.get('numberInputName').errors.max"> numberInputName cannot be more than 100. </span>
Solution 9 - Validation
I've found this as a solution. Create a custom validator as follow
minMax(control: FormControl) {
return parseInt(control.value) > 0 && parseInt(control.value) <=5 ? null : {
minMax: true
}
}
and under constructor include the below code
this.customForm= _builder.group({
'number': [null, Validators.compose([Validators.required, this.minMax])],
});
where customForm is a FormGroup and _builder is a FormBuilder.
Solution 10 - Validation
Angular has min and max validators but only for Reactive Forms. As it says in the docs: "The validator exists only as a function and not as a directive."
To be able to use these validators in template-driven forms you need to create custom directives. In my implementation i use @HostBinding
to also apply the HTML min
/max
-attributes. My selector
s are also quite specific to prevent validation running on custom form controls that implements ControlValueAccessor
with a min
or max
input (e.g. MatDatePickerInput)
min-validator:
import { Directive, HostBinding, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator, Validators } from '@angular/forms';
@Directive({
selector: 'input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]',
providers: [{ provide: NG_VALIDATORS, useExisting: MinValidatorDirective, multi: true }]
})
export class MinValidatorDirective implements Validator {
@HostBinding('attr.min') @Input() min: number;
constructor() { }
validate(control: AbstractControl): ValidationErrors | null {
const validator = Validators.min(this.min);
return validator(control);
}
}
max-validator:
import { Directive, HostBinding, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator, Validators } from '@angular/forms';
@Directive({
selector: 'input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]',
providers: [{ provide: NG_VALIDATORS, useExisting: MaxValidatorDirective, multi: true }]
})
export class MaxValidatorDirective implements Validator {
@HostBinding('attr.max') @Input() max: number;
constructor() { }
validate(control: AbstractControl): ValidationErrors | null {
const validator = Validators.max(this.max);
return validator(control);
}
}
Solution 11 - Validation
Angular 6 supports min & max validators: https://angular.io/api/forms/Validators
You can use those for static & dynamic values.
Static:
<input min="0" max="5">
Dynamic:
<input [min]="someMinValue" [max]="someMaxValue">
Solution 12 - Validation
Angualr itself provide a min and max number validation functionality.
Example - we have a field like age range then see the use of validation.
age_range : ['', Validators.min(1), Validators.max(18)]]
the age always be between 1 to 18.
Solution 13 - Validation
In latest Angular versions, min and max are already added. Here is the link: https://angular.io/api/forms/Validators#max
This is how I used Max validator in my project:
<mat-form-field class="globalInput">
<input (change)="CalculateAmount()" matInput placeholder="Quantity" name="productQuantity" type="number" [formControl]="quantityFormControl">
</mat-form-field>
<mat-error *ngIf="quantityFormControl.hasError('max')">
Only <strong>{{productQuantity}}</strong> available!
</mat-error>
Initialize the form control and add the validator in the component:
quantityFormControl = new FormControl('', Validators.max(15));
You can also set validator dynamically on an event like this:
quantityFormControl = new FormControl();
OnProductSelected(){
this.quantityFormControl.setValidators(Validators.max(this.someVariable));
}
Hope it helps.
Solution 14 - Validation
In my template driven form (Angular 6) I have the following workaround:
<div class='col-sm-2 form-group'>
<label for='amount'>Amount</label>
<input type='number'
id='amount'
name='amount'
required
[ngModel] = 1
[pattern] = "'^[1-9][0-9]*$'"
class='form-control'
#amountInput='ngModel'/>
<span class='text-danger' *ngIf="amountInput.touched && amountInput.invalid">
<p *ngIf="amountInput.errors?.required">This field is <b>required</b>!</p>
<p *ngIf="amountInput.errors?.pattern">This minimum amount is <b>1</b>!</p>
</span>
</div>
Alot of the above examples make use of directives and custom classes which do scale better in more complex forms, but if your looking for a simple numeric min, utilize pattern
as a directive and impose a regex restriction on positive numbers only.
Solution 15 - Validation
Find the custom validator for min number validation. The selector name of our directive is customMin.
custom-min-validator.directive.ts
import { Directive, Input } from '@angular/core';
import { NG_VALIDATORS, Validator, FormControl } from '@angular/forms';
@Directive({
selector: '[customMin][formControlName],[customMin][formControl],[customMin][ngModel]',
providers: [{provide: NG_VALIDATORS, useExisting: CustomMinDirective, multi: true}]
})
export class CustomMinDirective implements Validator {
@Input()
customMin: number;
validate(c: FormControl): {[key: string]: any} {
let v = c.value;
return ( v < this.customMin)? {"customMin": true} : null;
}
}
Find the custom validator for max number validation. The selector name of our directive is customMax.
custom-max-validator.directive.ts
import { Directive, Input } from '@angular/core';
import { NG_VALIDATORS, Validator, FormControl } from '@angular/forms';
@Directive({
selector: '[customMax][formControlName],[customMax][formControl],[customMax][ngModel]',
providers: [{provide: NG_VALIDATORS, useExisting: CustomMaxDirective, multi: true}]
})
export class CustomMaxDirective implements Validator {
@Input()
customMax: number;
validate(c: FormControl): {[key: string]: any} {
let v = c.value;
return ( v > this.customMax)? {"customMax": true} : null;
}
}
We can use customMax with formControlName, formControl and ngModel attributes.
Using Custom Min and Max Validator in Template-driven Form
We will use our custom min and max validator in template-driven form. For min number validation we have customMin attribute and for max number validation we have customMax attribute. Now find the code snippet for validation.
<input name="num1" [ngModel]="user.num1" customMin="15" #numberOne="ngModel">
<input name="num2" [ngModel]="user.num2" customMax="50" #numberTwo="ngModel">
We can show validation error messages as following.
<div *ngIf="numberOne.errors?.customMin">
Minimum required number is 15.
</div>
<div *ngIf="numberTwo.errors?.customMax">
Maximum number can be 50.
</div>
To assign min and max number we can also use property biding. Suppose we have following component properties.
minNum = 15;
maxNum = 50;
Now use property binding for customMin and customMax as following.
<input name="num1" [ngModel]="user.num1" [customMin]="minNum" #numberOne="ngModel">
<input name="num2" [ngModel]="user.num2" [customMax]="maxNum" #numberTwo="ngModel">
Solution 16 - Validation
My version for strict compilers
import { Directive, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms';
@Directive({
selector: '[appMinValidator]',
providers: [{ provide: NG_VALIDATORS, useExisting: MinValidatorDirective, multi: true }]
})
export class MinValidatorDirective implements Validator {
@Input()
appMinValidator!: number;
validate(control: AbstractControl): ValidationErrors | null {
return (control.value as number < this.appMinValidator) ? { appMinValidator: true } : null;
}
}
Solution 17 - Validation
USE
Validators.min(5)
It can be used while creating a formGroup variable along with other validators, as in
dueAmount:
['', [Validators.required, Validators.pattern(/^[+]?([0-9]+(?:[\.][0-9]*)?|\.[0-9]+)$/), Validators.min(5)]]
Not sure if it is in Angular 2, but is available in Angular 5
Solution 18 - Validation
I've added a max
validation to amd's great answer.
import { Directive, Input, forwardRef } from '@angular/core'
import { NG_VALIDATORS, Validator, AbstractControl, Validators } from '@angular/forms'
/*
* This is a wrapper for [min] and [max], used to work with template driven forms
*/
@Directive({
selector: '[min]',
providers: [{ provide: NG_VALIDATORS, useExisting: MinNumberValidator, multi: true }]
})
export class MinNumberValidator implements Validator {
@Input() min: number;
validate(control: AbstractControl): { [key: string]: any } {
return Validators.min(this.min)(control)
}
}
@Directive({
selector: '[max]',
providers: [{ provide: NG_VALIDATORS, useExisting: MaxNumberValidator, multi: true }]
})
export class MaxNumberValidator implements Validator {
@Input() max: number;
validate(control: AbstractControl): { [key: string]: any } {
return Validators.max(this.max)(control)
}
}
Solution 19 - Validation
This question has already been answered. I'd like to extend the answer from @amd. Sometimes you might need a default value.
For example, to validate against a specific value, I'd like to provide it as follows-
<input integerMinValue="20" >
But the minimum value of a 32 bit signed integer is -2147483648. To validate against this value, I don't like to provide it. I'd like to write as follows-
<input integerMinValue >
To achieve this you can write your directive as follows
import {Directive, Input} from '@angular/core';
import {AbstractControl, NG_VALIDATORS, ValidationErrors, Validator, Validators} from '@angular/forms';
@Directive({
selector: '[integerMinValue]',
providers: [{provide: NG_VALIDATORS, useExisting: IntegerMinValidatorDirective, multi: true}]
})
export class IntegerMinValidatorDirective implements Validator {
private minValue = -2147483648;
@Input('integerMinValue') set min(value: number) {
if (value) {
this.minValue = +value;
}
}
validate(control: AbstractControl): ValidationErrors | null {
return Validators.min(this.minValue)(control);
}
}
Solution 20 - Validation
In your code you are using min
and not minlength
. Please also notice that this will not validate if a number is > 0 but its length.