Skip to content

Instantly share code, notes, and snippets.

@ahmeti
Last active September 18, 2024 02:44
Show Gist options
  • Save ahmeti/5ca97ec41f6a48ef699ee6606560d1f7 to your computer and use it in GitHub Desktop.
Save ahmeti/5ca97ec41f6a48ef699ee6606560d1f7 to your computer and use it in GitHub Desktop.
Angular 5 - Only Number Input, Only Number Decimal

Angular 5,6,7,8,9 - Only Number Input, Only Decimal Input

v1 Online Demo: https://codesandbox.io/s/v1-angular-numeric-ljwlb

v1 Gist : https://gist.github.com/ahmeti/5ca97ec41f6a48ef699ee6606560d1f7/e36956cc355ad1bad6898e9eede5d7a9219947e8

v2 Online Demo: https://codesandbox.io/s/v2-angular-numeric-3w2wr

Allow Only Numbers (Without Decimal)

<input numeric type="text">

Allow Numbers & Only Two Decimals [0-9] (With Decimal Limit)

<input numeric decimals="2" type="text">
// Version 2

import {
    Directive,
    ElementRef,
    HostListener,
    Input
} from "@angular/core";

@Directive({
    selector: "[numeric]"
})
export class NumericDirective {
    @Input("decimals") decimals: int = 0;

    private check(value: string) {
        if (this.decimals <= 0) {
            return String(value).match(new RegExp(/^\d+$/));
        } else {
            var regExpString =
                "^\\s*((\\d+(\\.\\d{0," +
                this.decimals +
                "})?)|((\\d*(\\.\\d{1," +
                this.decimals +
                "}))))\\s*$";
            return String(value).match(new RegExp(regExpString));
        }
    }

    private run(oldValue) {
        setTimeout(() => {
            let currentValue: string = this.el.nativeElement.value;
            if (currentValue !== '' && !this.check(currentValue)) {
                this.el.nativeElement.value = oldValue;
            }
        });
    }

    constructor(private el: ElementRef) {}

    @HostListener("keydown", ["$event"])
    onKeyDown(event: KeyboardEvent) {
        this.run(this.el.nativeElement.value);
    }

    @HostListener("paste", ["$event"])
    onPaste(event: ClipboardEvent) {
        this.run(this.el.nativeElement.value);
    }

}
@isoftvn
Copy link

isoftvn commented Apr 4, 2020

@ahmeti how to allow copy & paste with this?

@rob-byram
Copy link

I am using the directive with two decimal places. If the existing value has two decimals (1.23), the directive does not let me type anything else, even if the cursor is in front of the decimal point. Is there a way to get around that?

@ahmeti
Copy link
Author

ahmeti commented Apr 10, 2020

Thank you guys. I updated and please check to a new version. @isoftvn @rob-byram

@rob-byram
Copy link

@ahmeti, thank you so much!! I spent a good portion of today trying (unsuccessfully) to do a workaround that also dealt with the "selected" characters. Your update is super simple and works like a charm!

@leidy777
Copy link

@ahmeti, Hey thanks so much! It has been quite useful!

@carlosesteban55
Copy link

carlosesteban55 commented Jun 11, 2020

@ahmeti How can I change the RegExp to accept negative numbers? Equal that now, but accpeting one or none "-" at first.

Thanks so much.

@carlosesteban55
Copy link

@ahmeti How to make this directive work for negative values

+1

@ahmeti
Copy link
Author

ahmeti commented Jun 11, 2020

Now supports negative values.
But i don't want to change base directive.
I added negative prop in component. If you want you can use the directive bellow.

@albertThinkpalm @carlosesteban55

<input numeric type="text" negative="1" />
import { Directive, ElementRef, HostListener, Input } from "@angular/core";

@Directive({
  selector: "[numeric]"
})
export class NumericDirective {
  @Input("decimals") decimals: int = 0;
  @Input("negative") negative: int = 0;

  private checkAllowNegative(value: string) {
    if (this.decimals <= 0) {
      return String(value).match(new RegExp(/^-?\d+$/));
    } else {
      var regExpString =
        "^-?\\s*((\\d+(\\.\\d{0," +
        this.decimals +
        "})?)|((\\d*(\\.\\d{1," +
        this.decimals +
        "}))))\\s*$";
      return String(value).match(new RegExp(regExpString));
    }
  }

  private check(value: string) {
    if (this.decimals <= 0) {
      return String(value).match(new RegExp(/^\d+$/));
    } else {
      var regExpString =
        "^\\s*((\\d+(\\.\\d{0," +
        this.decimals +
        "})?)|((\\d*(\\.\\d{1," +
        this.decimals +
        "}))))\\s*$";
      return String(value).match(new RegExp(regExpString));
    }
  }

  private run(oldValue) {
    setTimeout(() => {
      let currentValue: string = this.el.nativeElement.value;
      let allowNegative = this.negative > 0 ? true : false;

      if (allowNegative) {
        if (
          !["", "-"].includes(currentValue) &&
          !this.checkAllowNegative(currentValue)
        ) {
          this.el.nativeElement.value = oldValue;
        }
      } else {
        if (currentValue !== "" && !this.check(currentValue)) {
          this.el.nativeElement.value = oldValue;
        }
      }
    });
  }

  constructor(private el: ElementRef) {}

  @HostListener("keydown", ["$event"])
  onKeyDown(event: KeyboardEvent) {
    this.run(this.el.nativeElement.value);
  }

  @HostListener("paste", ["$event"])
  onPaste(event: ClipboardEvent) {
    this.run(this.el.nativeElement.value);
  }
}

@carlosesteban55
Copy link

Thanks a lot @ahmeti!!

@mavazca
Copy link

mavazca commented Jun 16, 2020

Now supports negative values and separator="," default "."

<input numeric type="text" decimals="2" negative="1" separator=","/>
import { Directive, ElementRef, HostListener, Input } from "@angular/core";

@Directive({
  selector: "[numeric]"
})
export class NumericDirective {
  @Input("decimals") decimals: number = 0;
  @Input("negative") negative: number = 0;
  @Input("separator") separator: string = ".";

  private checkAllowNegative(value: string) {
    if (this.decimals <= 0) {
      return String(value).match(new RegExp(/^-?\d+$/));
    } else {
      var regExpString =
        "^-?\\s*((\\d+(\\"+ this.separator +"\\d{0," +
        this.decimals +
        "})?)|((\\d*(\\"+ this.separator +"\\d{1," +
        this.decimals +
        "}))))\\s*$";
      return String(value).match(new RegExp(regExpString));
    }
  }

  private check(value: string) {
    if (this.decimals <= 0) {
      return String(value).match(new RegExp(/^\d+$/));
    } else {
      var regExpString =
        "^\\s*((\\d+(\\"+ this.separator +"\\d{0," +
        this.decimals +
        "})?)|((\\d*(\\"+ this.separator +"\\d{1," +
        this.decimals +
        "}))))\\s*$";
      return String(value).match(new RegExp(regExpString));
    }
  }

  private run(oldValue) {
    setTimeout(() => {
      let currentValue: string = this.el.nativeElement.value;
      let allowNegative = this.negative > 0 ? true : false;

      if (allowNegative) {
        if (
          !["", "-"].includes(currentValue) &&
          !this.checkAllowNegative(currentValue)
        ) {
          this.el.nativeElement.value = oldValue;
        }
      } else {
        if (currentValue !== "" && !this.check(currentValue)) {
          this.el.nativeElement.value = oldValue;
        }
      }
    });
  }

  constructor(private el: ElementRef) {}

  @HostListener("keydown", ["$event"])
  onKeyDown(event: KeyboardEvent) {
    this.run(this.el.nativeElement.value);
  }

  @HostListener("paste", ["$event"])
  onPaste(event: ClipboardEvent) {
    this.run(this.el.nativeElement.value);
  }
}

@mavazca
Copy link

mavazca commented Jun 16, 2020

Thanks a lot @ahmeti!!

@Vinoth-deva
Copy link

thanks works as Expected, but only thing is if i use [(ngmodel)] the textbox value is not matched with ngmodel

@Asad1592
Copy link

Asad1592 commented Aug 3, 2020

@ahmeti It's working fine on Chrome but in Firefox and Internet explorer its not working. Can you please suggest?

@karan2004
Copy link

it is not working in mobile device

@shivaji33
Copy link

thanks works as Expected, but only thing is if i use [(ngmodel)] the textbox value is not matched with ngmodel

Same issue

@shivaji33
Copy link

shivaji33 commented Apr 20, 2021

thanks works as Expected, but only thing is if i use [(ngmodel)] the textbox value is not matched with ngmodel

Same issue

please set value to ngControl

private run(oldValue) {
setTimeout(() => {
let currentValue: string = this.el.nativeElement.value;
let allowNegative = this.negative > 0;
if (allowNegative) {
if (!['', '-'].includes(currentValue) && !this.checkAllowNegative(currentValue)) {
this.ngControl.control.setValue(oldValue, {emitEvent: false})
}
} else {
if (currentValue !== '' && !this.check(currentValue)) {
this.ngControl.control.setValue(oldValue, {emitEvent: false})
}
}
});
}

@rbrijesh
Copy link

rbrijesh commented Jun 2, 2021

I want to add prevent default function when user try to enter alphabetical values and other things is that when I try to add numbers at max typing speed it accept more then given decimal values.
For example Input accept 2 decimal values but when try to forcefully enter the values it accepts more then 2 decimals. Any solution will help me to prevent this thing.
Thanks in advance.

@sreekan2
Copy link

How are you removing the zeros in the front (00012)?

@lujian98
Copy link

To access ngControl, add @Optional() @Self() private ngControl: NgControl, to constructor:

 constructor(
    @Optional() @Self() private ngControl: NgControl,
    private el: ElementRef<HTMLInputElement | HTMLTextAreaElement>,
  ) { }

    if (this.ngControl) {
      this.ngControl.control.patchValue(oldValue, { emitEvent: false, onlySelf: true });
    }

@LSzelecsenyi
Copy link

LSzelecsenyi commented Dec 2, 2022

Thank you, very useful!
As @rbrijesh mentioned, if one types more than the maximum decimals, the input does not show it, but the value of the input is longer by one number. Is there a fix for this? I could not find what causes it.

@LSzelecsenyi
Copy link

Here is a fixed version. It uses NgControl.valueChanges insted of @HostListeners which fixes the extra character at the end of the input when submitting the form.

`

import { Directive, Input, OnInit } from '@angular/core';
import { NgControl } from '@angular/forms';
import { distinctUntilChanged, pairwise, Subscription } from 'rxjs';

@Directive({
    selector: '[restrictedDecimals]'
})
export class InstrumentDecimalsDirective implements OnInit {
    @Input() decimals = 0;
    @Input() negative = 0;
    @Input() separator = ',';
    valueSubscription: Subscription;

    constructor(public ngControl: NgControl) {}

    ngOnInit(): void {
        this.ngControl.valueChanges.pipe(distinctUntilChanged(), pairwise()).subscribe(([oldValue, newValue]) => {
            this.runCheck(oldValue, newValue);
        });
    }

    private runCheck(oldValue, newValue) {
        const allowNegative = this.negative > 0 ? true : false;

        if (allowNegative) {
            if (!['', '-'].includes(newValue) && !this.checkAllowNegative(newValue)) {
                this.ngControl.control.setValue(oldValue);
            }
        } else {
            if (newValue !== '' && !this.check(newValue)) {
                this.ngControl.control.setValue(oldValue);
            }
        }
    }

    private checkAllowNegative(value: string) {
        if (this.decimals <= 0) {
            return new RegExp(/^-?\d+$/).exec(String(value));
        } else {
            const regExpString =
                '^-?\\s*((\\d+(\\' + this.separator + '\\d{0,' + this.decimals + '})?)|((\\d*(\\' + this.separator + '\\d{1,' + this.decimals + '}))))\\s*$';
            return new RegExp(regExpString).exec(String(value));
        }
    }

    private check(value: string) {
        if (this.decimals <= 0) {
            return new RegExp(/^\d+$/).exec(String(value));
        } else {
            const regExpString =
                '^\\s*((\\d+(\\' + this.separator + '\\d{0,' + this.decimals + '})?)|((\\d*(\\' + this.separator + '\\d{1,' + this.decimals + '}))))\\s*$';
            return new RegExp(regExpString).exec(String(value));
        }
    }
}

`

@akshaywadatkar
Copy link

is there any new update for 13

@LSzelecsenyi
Copy link

is there any new update for 13

it works with angular v14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment