Skip to content

Instantly share code, notes, and snippets.

@ahmeti
Last active September 18, 2024 02:44
Show Gist options
  • Select an option

  • Save ahmeti/5ca97ec41f6a48ef699ee6606560d1f7 to your computer and use it in GitHub Desktop.

Select an option

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

}
@zeeshan4418

zeeshan4418 commented Nov 11, 2019

Copy link
Copy Markdown

it's not working fine when i am entering text this field accepting text

@Jozx

Jozx commented Dec 6, 2019

Copy link
Copy Markdown

It's work fine.

I have a question, if I need put a variable inside, how could I do?

I try

x:number = 0;
 y:number = 2;
private regex = {
        number: new RegExp(/^\d+$/),
        decimal : new RegExp("^\\d{"+ x +","+ y +"}$", "g")
    };

also
decimal: new RegExp(/^\d*\,?\d{"+ x +","+ y +"}$/g)

but didn't work

Any suggestion? Because I need use that like a parameter (the user put how many decimal want to use)

@ahmeti

ahmeti commented Dec 11, 2019

Copy link
Copy Markdown
Author

@darwinguz Thank you. Added "Delete" key.

@Jozx thank you for your message. I updated the directive and added a decimal limit option.

The directive is much cleaner than before :)

@albertThinkpalm

albertThinkpalm commented Feb 3, 2020

Copy link
Copy Markdown

@ahmeti How to make this directive work for negative values

@ShubhamRK96

ShubhamRK96 commented Feb 4, 2020

Copy link
Copy Markdown

@ahmeti Thank you so much. It's perfectly worked.

@aruribadraiah

Copy link
Copy Markdown

when it is putting application level it is not working in individual module it is working file , so how to application levelel ? i.e. if we put in app.module.ts file importing file and declaration of file not working..

@isoftvn

isoftvn commented Apr 4, 2020

Copy link
Copy Markdown

@ahmeti how to allow copy & paste with this?

@rob-byram

Copy link
Copy Markdown

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

ahmeti commented Apr 10, 2020

Copy link
Copy Markdown
Author

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

@rob-byram

Copy link
Copy Markdown

@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
Copy Markdown

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

@carlosesteban55

carlosesteban55 commented Jun 11, 2020

Copy link
Copy Markdown

@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
Copy Markdown

@ahmeti How to make this directive work for negative values

+1

@ahmeti

ahmeti commented Jun 11, 2020

Copy link
Copy Markdown
Author

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
Copy Markdown

Thanks a lot @ahmeti!!

@mavazca

mavazca commented Jun 16, 2020

Copy link
Copy Markdown

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

mavazca commented Jun 16, 2020

Copy link
Copy Markdown

Thanks a lot @ahmeti!!

@Vinoth-deva

Copy link
Copy Markdown

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

@Asad1592

Asad1592 commented Aug 3, 2020

Copy link
Copy Markdown

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

@karan2004

Copy link
Copy Markdown

it is not working in mobile device

@shivaji33

Copy link
Copy Markdown

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

Same issue

@shivaji33

shivaji33 commented Apr 20, 2021

Copy link
Copy Markdown

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

rbrijesh commented Jun 2, 2021

Copy link
Copy Markdown

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
Copy Markdown

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

@lujian98

Copy link
Copy Markdown

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

LSzelecsenyi commented Dec 2, 2022

Copy link
Copy Markdown

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
Copy Markdown

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
Copy Markdown

is there any new update for 13

@LSzelecsenyi

Copy link
Copy Markdown

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