Min and max value of input in angular4 application

Viewed 160454

I have an angular4 application with a form. In this one I have an input to enter a percentage. So, I want to block the input with value between 0 and 100. I tried to add min="0" and max="100" but I can yet enter an number higher than 100 or smaller than 0.

template

<md-input-container>
  <input type="number" 
    maxlength="3" 
    min="0" 
    max="100" 
    required 
    mdInput 
    placeholder="Charge" 
    [(ngModel)]="rateInput" 
    name="rateInput">
  <md-error>Required field</md-error>
</md-input-container>

Do you know how I can do this ?

10 Answers

You can control with a change event if the input is within your range, if it is not in the range you assign 0.

<md-input-container>
  <input type="number" 
    maxlength="3" 
    min="0" 
    max="100" 
    required 
    mdInput 
    placeholder="Charge" 
    [(ngModel)]="rateInput"
    (change)= "rateInput < 0 ? rateInput = 0 : rateInput; rateInput > 100 ? rateInput = 0 : rateIntput;"
    name="rateInput">
  <md-error>Required field</md-error>
</md-input-container>

Simply do this in angular2+ by adding (onkeypress)

<input type="number" 
    maxlength="3" 
    min="0" 
    max="100" 
    required 
    mdInput 
    placeholder="Charge" 
    [(ngModel)]="rateInput"
    (onkeypress)="return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57"
    name="rateInput">

Tested on Angular 7

I did a workaround with ngModelChange. In my case I wanted to avoid that users set a year earlier than current year. In html

<div class="col">
      <label>Year<span *ngIf="yearInvalid" class="badge badge-danger ml-1"> Year is invalid</span></label>

      <input class="form-control" type="number" id="year" name="year" #year="ngModel"
      [(ngModel)]="projection.year" required pattern="(?:19|20)[0-9]{2}"
      (ngModelChange)="checkYear($event)" />
</div>

And in angular I just check on model change:

checkYear(e) {
    if (e < new Date().getFullYear()) {
      this.yearInvalid = true;
    }
    else {
      this.yearInvalid = false;
    }
  }

I think it can work very good with 0-100 or any other check that you need to do.

You can write a directive to listen the change event on the input and reset the value to the min value if it is too low. StackBlitz

@HostListener('change') onChange() {
  const min = +this.elementRef.nativeElement.getAttribute('min');

  if (this.valueIsLessThanMin(min, +this.elementRef.nativeElement.value)) {
    this.renderer2.setProperty(
      this.elementRef.nativeElement,
      'value',
      min + ''
    );
  }
}

Also listen for the ngModelChange event to do the same when the form value is set.

@HostListener('ngModelChange', ['$event'])
onModelChange(value: number) {
  const min = +this.elementRef.nativeElement.getAttribute('min');
  if (this.valueIsLessThanMin(min, value)) {
    const formControl = this.formControlName
      ? this.formControlName.control
      : this.formControlDirective.control;

    if (formControl) {
      if (formControl.updateOn === 'change') {
        console.warn(
          `minValueDirective: form control ${this.formControlName.name} is set to update on change
          this can cause issues with min update values.`
        );
      }
      formControl.reset(min);
    }
  }
}

Full code:

import {
  Directive,
  ElementRef,
  HostListener,
  Optional,
  Renderer2,
  Self
} from "@angular/core";
import { FormControlDirective, FormControlName } from "@angular/forms";

@Directive({
  // tslint:disable-next-line: directive-selector
  selector: "input[minValue][min][type=number]"
})
export class MinValueDirective {
  @HostListener("change") onChange() {
    const min = +this.elementRef.nativeElement.getAttribute("min");

    if (this.valueIsLessThanMin(min, +this.elementRef.nativeElement.value)) {
      this.renderer2.setProperty(
        this.elementRef.nativeElement,
        "value",
        min + ""
      );
    }
  }

  // if input is a form control validate on model change
  @HostListener("ngModelChange", ["$event"])
  onModelChange(value: number) {
    const min = +this.elementRef.nativeElement.getAttribute("min");
    if (this.valueIsLessThanMin(min, value)) {
      const formControl = this.formControlName
        ? this.formControlName.control
        : this.formControlDirective.control;

      if (formControl) {
        if (formControl.updateOn === "change") {
          console.warn(
            `minValueDirective: form control ${
              this.formControlName.name
            } is set to update on change
              this can cause issues with min update values.`
          );
        }
        formControl.reset(min);
      }
    }
  }

  constructor(
    private elementRef: ElementRef<HTMLInputElement>,
    private renderer2: Renderer2,
    @Optional() @Self() private formControlName: FormControlName,
    @Optional() @Self() private formControlDirective: FormControlDirective
  ) {}

  private valueIsLessThanMin(min: any, value: number): boolean {
    return typeof min === "number" && value && value < min;
  }
}

Make sure to use this with the form control set to updateOn blur or the user won't be able to enter a +1 digit number if the first digit is below the min value.

 this.formGroup = this.formBuilder.group({
    test: [
      null,
      {
        updateOn: 'blur',
        validators: [Validators.min(5)]
      }
    ]
  });

[I assume the reader has basic knowledge of Angular2+ and Forms]

It is easy to show a numerical input and put the limits, but you have to also take care of things may happen out of your predictions.

  1. Implement the tag in your 'html':
<input type="number" [min]="0.00" [max]="100.00" [step]="0.01" formControlName="rateFC">
  1. But as Adrien said, still user can enter manually a wrong number. You can validate input by Validators easily. In your '.ts':
import { FormGroup, FormControl, Validators } from '@angular/forms';

//many other things...

this.myFG = new FormGroup({
   //other form controls...,
   rateFC  : new FormControl(0, [Validators.min(0), Validators.max(100)])
});
  1. Up to now everything is ok, but it is better to let the user know the input is wrong, then draw a red line around the invalid input element by adding to your style:
.form-control.ng-touched.ng-invalid{
    border:2px solid red;
}
  1. And to make it perfect, prevent the user to submit the wrong data.
<button type="submit" [disabled]="!myFG.valid">Submit</button>

Most simple approach in Template driven forms for min/max validation with out using reactive forms and building any directive, would be to use pattern attribute of html. This has already been explained and answered here please look https://stackoverflow.com/a/63312336/14069524

If you are looking to validate length use minLength and maxLength instead.

Related