27

I have an input and the type is number. I want to set min and max, if the input is out of this range (< min or >max), the error will be displayed.

My problem is that if the input is not empty but invalid, the error disappears (example: min =10, max =20, input =2000, still no error displays).

I search on this site and there is a post about validation but the solution is to use < md-input-container> and < md-error>. I don't want to use < md-input-container> and < md-error>.

Reference:here

Is there anyway to solve my problem?

My add-hero.component.html:

<div class="publishedYear">
<label>Publish Year: </label>
<input type="number" id="PublishedYear" name="PublishedYear" required min="10" max="20" 
[(ngModel)]="hero.PublishedYear" #publishedYear="ngModel">
<p class="alert alert-danger invalid" *ngIf="publishedYear.errors">Invalid Published Year</p>
</div>

My Hero.ts

export class Hero {
Id: number;
Name: string;
PublishedYear: number;
Complete?: boolean;
}
anhtv13
  • 1,636
  • 4
  • 30
  • 51
  • this has probably not much to do with the template moreover with your constructor and your component structure could you show some more code? – Synoon Oct 24 '18 at 08:58
  • @Synoon nothing in my constructor, I added my Hero.ts to the question. – anhtv13 Oct 24 '18 at 09:05
  • Either you should have ng-form in your html, and use for input or write a method to validate the input in component, when the user is done giving input value and update the variable to be used in *ngIf condition. – Sasi Kumar M Oct 24 '18 at 09:10
  • @SasiKumarM can you post an answer about having ng-form in html? I don't use any ng-form, just simple < div>. Thank you in advanced. – anhtv13 Oct 24 '18 at 09:15

6 Answers6

22

To your solutions when you are using input type="number" setting min and max will only stop allowing user when he will increment or decrement number using the scroller. but when user will type the number directly in the text it won't show any error

to do that there are 2 solutions

1) Add form control to your input using angular form validation there will be a couple of examples online

2) Call a function on on-change of a text box or on button click to validate the number entered by a user matches your expression in ts file.

using form validation you need to do

myForm = new FormGroup({}) // Instantiating our form

constructor(private fb: FormBuilder){ // Injecting the ReactiveForms FormBuilder.
  this.myForm = fb.group({
    // Adding the "myNum" input to our FormGroup along with its min-max Validators.
    'myNum': ['', [Validators.min(5), Validators.max(10)]] 
  })
}

and in HTML

<form [formGroup]="myForm"> <!-- Binding myForm to the form in the template -->
    <label for="myNum">Some Val</label>
    <!-- formControlName binds our myNum field declared in the ts code. -->
    <input type='number' id="myNum" formControlName="myNum">
    <div *ngIf="myForm.controls['myNum'].hasError('max')">
          Minimum required number is 15.
    </div> 
</form>
CodeChanger
  • 7,953
  • 5
  • 49
  • 80
Hrishikesh Kale
  • 6,140
  • 4
  • 18
  • 27
9

See the sample code in stackblitz

import { Component } from '@angular/core';

import { Directive, Input, forwardRef } from "@angular/core";
import {
    Validator, AbstractControl, NG_VALIDATORS, Validators, ValidatorFn
    } from "@angular/forms";

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  hero: any = {};
}


@Directive({
    selector: "[min][formControlName],[min][formControl],[min][ngModel]",
    providers: [
        { provide: NG_VALIDATORS,
            useExisting: forwardRef(() => MinDirective),
            multi: true }
    ]
})
export class MinDirective implements Validator {
    private _validator: ValidatorFn;
    @Input() public set min(value: string) {
        this._validator = Validators.min(parseInt(value, 10));
    }

    public validate(control: AbstractControl): { [key: string]: any } {
        return this._validator(control);
    }
}

@Directive({
    selector: "[max][formControlName],[max][formControl],[max][ngModel]",
    providers: [
        { provide: NG_VALIDATORS,
            useExisting: forwardRef(() => MaxDirective),
            multi: true }
    ]
})
export class MaxDirective implements Validator {
    private _validator: ValidatorFn;
    @Input() public set max(value: string) {
        this._validator = Validators.max(parseInt(value, 10));
    }

    public validate(control: AbstractControl): { [key: string]: any } {
        return this._validator(control);
    }
}

<input type="number" [(ngModel)]="hero.count" name="count" #count="ngModel" required min="1" max="100">

<p *ngIf="count.invalid">Invalid Published Year</p>

Add both directive to declarations. This should work for template driven forms

Tibin Thomas
  • 742
  • 5
  • 12
  • your solution works but I found another way to solve the problem. Btw, your solution is highly appreciated, thank you for your effort. – anhtv13 Oct 25 '18 at 02:05
  • The selector should be more restrictive, otherwise it will conflict with `mat-datepicker` which has its own `min` and `max` validators. For example `input[type='number'][max][formControlName],...` to apply to number inputs. – Huon Jan 21 '21 at 05:42
6

Thanks for all of your answers above and your efforts. After a few hours doing research, I finally got an answer from this : page

If you have any problem like me, here is the answer.

My .html file:

<form [formGroup]="myForm">
<label for="publishedYear">Publish Year: </label>
<input type="number" id="PublishedYear" formControlName="publishedYear">
<div *ngIf="f.publishedYear.errors">
  Invalid Published Year
</div>

My .ts file:

import { Component, OnInit, Input } from '@angular/core';
import { Hero } from '../Hero';
import { HeroService } from '../hero.service';
import { Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from 
'@angular/forms';

@Component({
selector: 'app-hero-add',
templateUrl: './hero-add.component.html',
styleUrls: ['./hero-add.component.css']
})
export class HeroAddComponent implements OnInit {

hero: Hero = new Hero();

myForm = new FormGroup({}) // Instantiating our form

get f() { return this.myForm.controls; }

constructor(private heroService: HeroService, private router: Router, private 
formBuilder: FormBuilder) {
    this.myForm = formBuilder.group({     
    publishedYear: ['', [Validators.min(1990), Validators.max(2018)]]
});
}

  ngOnInit() {
  }     
}
anhtv13
  • 1,636
  • 4
  • 30
  • 51
5

The most simple approach dealing with min max validations in template driven form is using pattern attribute of html and assign it a regex number for example for range 0-24 the input would be

<input pattern="([0-9]|1[0-9]|2[0-4])" required type="number" id="monday" name="monday" [(ngModel)]="pullingStrategy.one" #monday="ngModel" />

So if the value is not in the range the form will be UNVALID If you wanna generate regex number for any other range use this Link https://3widgets.com/

1

use code in html and ts file

<input type="text" class="form-control" (input)="onSearchChange($event.target.value)">

onSearchChange(searchValue: string): void {  
  console.log(searchValue);
// validate the value here
}
safeena rasak
  • 390
  • 2
  • 5
  • 16
0

In case you add Validators.min & Validators.max to form control you can still enter string and it won't set any error.

Here is an example how to make sure it is numeric

public form = new FormGroup({
  control: new UntypedFormControl(undefined, {
    validators: [Validators.pattern(/^\d+$/)]
  })
});
Ssaskee
  • 1
  • 1