30

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;
}
4
  • this has probably not much to do with the template moreover with your constructor and your component structure could you show some more code? Commented Oct 24, 2018 at 8:58
  • @Synoon nothing in my constructor, I added my Hero.ts to the question. Commented Oct 24, 2018 at 9:05
  • Either you should have ng-form in your html, and use <mat-form-field> 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. Commented Oct 24, 2018 at 9: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. Commented Oct 24, 2018 at 9:15

6 Answers 6

24

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>
Sign up to request clarification or add additional context in comments.

7 Comments

let me try this, I'll tell you the result soon. Thank you in advanced.
what is num1 in the line "< div *ngIf="num1.errors">?
its a error message check when the condition is not satisfied it will show error
I got an error: "ERROR TypeError: Cannot read property 'errors' of undefined". So "num1" is undefined then it cannot get the "errors" of undefined object.
thank you, I finally solved the problem. Thank you for your effort, your solution is highly appreciated.
|
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

2 Comments

your solution works but I found another way to solve the problem. Btw, your solution is highly appreciated, thank you for your effort.
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.
7

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() {
  }     
}

3 Comments

How you are handling those error when the user enters a value like 2019?
@CodeChanger the div containing "Invalid Published Year" shows up.
Implemented this solution in my project and it works fine! Thanks!
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/

Comments

2

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+$/)]
  })
});

Comments

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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.