62

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:

enter image description here

enter image description here

So, to make the form result in an error when firstValue ngModel < 0, do I need to build a custom validator?

Community
  • 1
  • 1
David
  • 15,652
  • 26
  • 115
  • 156
  • `FirstValue < 0`, and ` – David Oct 04 '16 at 09:01
  • Yes you would need to. Check the source code here: https://github.com/angular/angular/blob/master/modules/%40angular/forms/src/directives/validators.ts Only those 4 validators mentioned by thoughtram article are supported OOTB. – Harry Ninh Oct 04 '16 at 09:08

20 Answers20

66

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}))
Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
Ankit Singh
  • 24,525
  • 11
  • 66
  • 89
  • 55
    why doesnt angular 2 simply support the built-in `min`/`max` properties of inputs?? this is highly unintuitive – phil294 Feb 25 '17 at 01:45
  • 26
    @Blauhirn Don't know, probably the guy writing the validators got bored halfway through :) – David May 10 '17 at 08:20
  • 33
    Angular 4 now supports min/max validators. https://angular.io/api/forms/Validators – Olezt Jun 15 '17 at 20:14
  • 10
    @Olezt, Angular 4 right now only supports min/max validators as functions and not as directives, which means they are completely useless in template-driven forms. This is a very weird decision. – JoannaFalkowska Mar 30 '18 at 13:54
  • 5
    @Senthe Support for template driven forms was added originally but it was a breaking change (and thus [rolled back](https://github.com/angular/angular/commit/3e685f98c666fcb9f0556ccb65d4b40716ac2985#diff-d60c63de232466b99bc9f5ab2bb39430)) as many people were already using `min` and `max` and not all of them were expecting validation from it. There is [this request](https://github.com/angular/angular/issues/16352) but it has a larger scope and may be slower to happen and so I added [a more tailored request](https://github.com/angular/angular/issues/23635) just now. – Pace May 01 '18 at 18:36
  • I am new to Angular and use Angular 5. Where do I add the validator extension and where do I initialise the 1numberControl` @Ankit? Thanks – nickornotto Jun 29 '18 at 12:09
33

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>
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
David
  • 15,652
  • 26
  • 115
  • 156
  • 3
    Link-only answers are [not useful](http://meta.stackexchange.com/q/225370/248731). – jonrsharpe Nov 30 '16 at 23:20
  • 19
    Well it sure was useful for me! – Vingtoft May 09 '17 at 12:35
  • Beware: it seems to present problems in the AOT version of my app. – cmolina Sep 21 '17 at 19:14
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/19007842) – Kumar Saurabh Mar 05 '18 at 12:38
  • 1
    @KumarSaurabh Agreed, it's better to include essential parts of the answer - but your review flagged this as "Recommend deletion?" – David Mar 12 '18 at 12:28
  • 1
    The link is very useful. However, it is technically a link-only answer, so I have edited to also include a small explanation and an example. – Alexei - check Codidact Apr 29 '18 at 10:46
  • Your answer is outdated. A fork for Angular 9+ is here: [ngx-custom-validators](https://github.com/rsaenen/ngx-custom-validators) – Mick Jul 02 '20 at 15:19
29

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
  • email
  • 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)]);
Sadid Khan
  • 1,836
  • 20
  • 35
24

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



  }

}
amd
  • 20,637
  • 6
  • 49
  • 67
  • It will be more helpful if you explain like answer above https://stackoverflow.com/a/44722596/3898339 – Deep 3015 Dec 26 '17 at 09:30
  • 1
    Exactly what I need when writng template driven forms! – scipper Jan 17 '20 at 10:20
  • 4
    With template driven forms this is the best solution. Created a stackblitz here: https://stackblitz.com/edit/angular-min-max-validation – spierala Mar 10 '20 at 14:20
  • 1
    N.B: you have to use brackets, as `[min]="0"` instead of `min="0"`, because directive wants a number, not a string. – Random May 10 '21 at 10:35
  • @amd how to call validate when min changes? Validate gets only called when control changes, but not the conditions. – alex351 May 09 '23 at 09:21
13

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)]]
});
shlomiLan
  • 659
  • 1
  • 9
  • 33
11

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;
    };
  }
Joshua Simon
  • 119
  • 1
  • 2
  • 5
    The validation **logic** exists in Angular, but they haven't exposed it as directives. So anyone doing template-driven forms will still need to write their own directives (as shown in @amd's answer), or use a library. – mamacdon Mar 13 '18 at 17:21
10

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!

Hiren Shah
  • 326
  • 1
  • 2
  • 11
Eugene Kulabuhov
  • 2,349
  • 1
  • 26
  • 25
6
  1. Switch to use reactive forms instead of template forms (they are just better), otherwise step 5 will be slightly different.
  2. 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 };
      }
    }
    
    }
    
  3. Import service into module.

  4. Add includes statement in component where it is to be used:

        import { NumberValidatorsService } from "app/common/number-validators.service";
    
  5. Add validators to form builder:

        this.myForm = this.fb.group({
          numberInputName: [0, [Validators.required, NumberValidatorsService.max(100), NumberValidatorsService.min(0)]],
        });
    
  6. In the template, you can display the errors as follows:

     <span *ngIf="myForm.get('numberInputName').errors.max">
             numberInputName cannot be more than 100. 
      </span>
    
trees_are_great
  • 3,881
  • 3
  • 31
  • 62
  • They are better until you need to wire up `disabled` at which point you find out that it's a massive hassle to get that to work properly. You end up having to create all sorts of complex subscriptions, set accessors, and change detectors just to dynamically disable your forms. Awful that Angular decided to leave out the ability to provide a function to the `disable` initialization in reactive form controls. – crush Jan 29 '19 at 12:53
4

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.

Kirubel
  • 1,461
  • 1
  • 14
  • 33
4

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 selectors 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);
  }
}

andbjer
  • 554
  • 2
  • 9
  • These are the most complete directives of the whole answer. It remains to check if the integrated Validators validates in all scenarios. – Abel Matos Mar 17 '22 at 22:11
3

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">
garfunkel61
  • 1,459
  • 12
  • 10
  • @sumitkanoje In Angular 5 the following worked for me in a template driven form: `this.templateItem1 = new FormControl('', [ Validators.required, Validators.pattern('[0-9]+'), Validators.min(1), Validators.max(10) ]);` For an input field that was required, allows numbers only, and must be in the range of [1-10]. In the HTML, the element is: `` (and exists within a `form-group` & `form` element), and then you can use the `form-control-feedback` with `templateItem1.errors .dirty .touched` for validation messages. – aspergillusOryzae Oct 12 '18 at 19:41
  • it isn't template driven form – Kostrzak Nov 25 '18 at 17:07
  • From Angular documentation about min and max: "The validator exists only as a function and not as a directive." Min max only work with reactive forms. – Alex G Feb 19 '21 at 10:27
3

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.

2

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.

Callat
  • 2,928
  • 5
  • 30
  • 47
1

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.

Sachin Parashar
  • 1,067
  • 2
  • 18
  • 28
1

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"> 
Rohan Shenoy
  • 805
  • 10
  • 23
  • This work for me, but you should edit the directive to cast to number because is comparing with string in "v > this.customMax" to "v > +this.customMax" because doen't work in angular 7 – Zaha Dec 05 '19 at 23:41
  • This can be because the input type might be equal to text instead of number. So you can either cast to a number or change the type to number – Rohan Shenoy Dec 06 '19 at 07:58
1

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;
  }

}
wutzebaer
  • 14,365
  • 19
  • 99
  • 170
0

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

Jignesh Joisar
  • 13,720
  • 5
  • 57
  • 57
0

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)
  }
}
noamyg
  • 2,747
  • 1
  • 23
  • 44
0

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);
    }

}
misbah
  • 179
  • 6
-3

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.

mapm
  • 1,315
  • 12
  • 14
  • 1
    The question was about how minlength is supported by NG2 validation while min is not, and if this is an intentional, out of the box thing - or just some piece of NG knowledge I'm missing. I know the difference between min and minlength :P – David Oct 04 '16 at 08:53