Angular2 - Input Field To Accept Only Numbers
Solution 1
You can use angular2 directives. Plunkr
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[OnlyNumber]'
})
export class OnlyNumber {
constructor(private el: ElementRef) { }
@Input() OnlyNumber: boolean;
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent> event;
if (this.OnlyNumber) {
if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+C
(e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+V
(e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+X
(e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
// Allow: home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
// let it happen, don't do anything
return;
}
// Ensure that it is a number and stop the keypress
if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
e.preventDefault();
}
}
}
}
and you need to write the directive name in your input as an attribute
<input OnlyNumber="true" />
don't forget to write your directive in declarations array of your module.
By using regex you would still need functional keys
export class OnlyNumber {
regexStr = '^[0-9]*$';
constructor(private el: ElementRef) { }
@Input() OnlyNumber: boolean;
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent> event;
if (this.OnlyNumber) {
if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
// Allow: Ctrl+A
(e.keyCode == 65 && e.ctrlKey === true) ||
// Allow: Ctrl+C
(e.keyCode == 67 && e.ctrlKey === true) ||
// Allow: Ctrl+V
(e.keyCode == 86 && e.ctrlKey === true) ||
// Allow: Ctrl+X
(e.keyCode == 88 && e.ctrlKey === true) ||
// Allow: home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
// let it happen, don't do anything
return;
}
let ch = String.fromCharCode(e.keyCode);
let regEx = new RegExp(this.regexStr);
if(regEx.test(ch))
return;
else
e.preventDefault();
}
}
}
Solution 2
If you don't want a directive
https://stackblitz.com/edit/numeric-only
in component.html
<input (keypress)="numberOnly($event)" type="text">
in component.ts
export class AppComponent {
numberOnly(event): boolean {
const charCode = (event.which) ? event.which : event.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57)) {
return false;
}
return true;
}
}
Solution 3
I know this is an old question, but since this is a common funcionality, I want to share the modifications I've made:
- Custom decimal separator (point or comma)
- Support for integers only or integer and decimals
- Support for positive numbers only or positives and negatives
- Validate minus sign(-) is in the beginning
- Support to mouse pasting (with some limitation though https://caniuse.com/#feat=clipboard)
- Support for Mac command key
-
Replace strings like ".33" and "33." for the correct versions: 0.33 and 33.0
import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ selector: '[NumbersOnly]' }) export class NumbersOnly { @Input() allowDecimals: boolean = true; @Input() allowSign: boolean = false; @Input() decimalSeparator: string = '.'; previousValue: string = ''; // -------------------------------------- // Regular expressions integerUnsigned: string = '^[0-9]*$'; integerSigned: string = '^-?[0-9]+$'; decimalUnsigned: string = '^[0-9]+(.[0-9]+)?$'; decimalSigned: string = '^-?[0-9]+(.[0-9]+)?$'; /** * Class constructor * @param hostElement */ constructor(private hostElement: ElementRef) { } /** * Event handler for host's change event * @param e */ @HostListener('change', ['$event']) onChange(e) { this.validateValue(this.hostElement.nativeElement.value); } /** * Event handler for host's paste event * @param e */ @HostListener('paste', ['$event']) onPaste(e) { // get and validate data from clipboard let value = e.clipboardData.getData('text/plain'); this.validateValue(value); e.preventDefault(); } /** * Event handler for host's keydown event * @param event */ @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent) { let cursorPosition: number = e.target['selectionStart']; let originalValue: string = e.target['value']; let key: string = this.getName(e); let controlOrCommand = (e.ctrlKey === true || e.metaKey === true); let signExists = originalValue.includes('-'); let separatorExists = originalValue.includes(this.decimalSeparator); // allowed keys apart from numeric characters let allowedKeys = [ 'Backspace', 'ArrowLeft', 'ArrowRight', 'Escape', 'Tab' ]; // when decimals are allowed, add // decimal separator to allowed codes when // its position is not close to the the sign (-. and .-) let separatorIsCloseToSign = (signExists && cursorPosition <= 1); if (this.allowDecimals && !separatorIsCloseToSign && !separatorExists) { if (this.decimalSeparator == '.') allowedKeys.push('.'); else allowedKeys.push(','); } // when minus sign is allowed, add its // key to allowed key only when the // cursor is in the first position, and // first character is different from // decimal separator let firstCharacterIsSeparator = (originalValue.charAt(0) != this.decimalSeparator); if (this.allowSign && !signExists && firstCharacterIsSeparator && cursorPosition == 0) { allowedKeys.push('-'); } // allow some non-numeric characters if (allowedKeys.indexOf(key) != -1 || // Allow: Ctrl+A and Command+A (key == 'a' && controlOrCommand) || // Allow: Ctrl+C and Command+C (key == 'c' && controlOrCommand) || // Allow: Ctrl+V and Command+V (key == 'v' && controlOrCommand) || // Allow: Ctrl+X and Command+X (key == 'x' && controlOrCommand)) { // let it happen, don't do anything return; } // save value before keydown event this.previousValue = originalValue; // allow number characters only let isNumber = (new RegExp(this.integerUnsigned)).test(key); if (isNumber) return; else e.preventDefault(); } /** * Test whether value is a valid number or not * @param value */ validateValue(value: string): void { // choose the appropiate regular expression let regex: string; if (!this.allowDecimals && !this.allowSign) regex = this.integerUnsigned; if (!this.allowDecimals && this.allowSign) regex = this.integerSigned; if (this.allowDecimals && !this.allowSign) regex = this.decimalUnsigned; if (this.allowDecimals && this.allowSign) regex = this.decimalSigned; // when a numbers begins with a decimal separator, // fix it adding a zero in the beginning let firstCharacter = value.charAt(0); if (firstCharacter == this.decimalSeparator) value = 0 + value; // when a numbers ends with a decimal separator, // fix it adding a zero in the end let lastCharacter = value.charAt(value.length-1); if (lastCharacter == this.decimalSeparator) value = value + 0; // test number with regular expression, when // number is invalid, replace it with a zero let valid: boolean = (new RegExp(regex)).test(value); this.hostElement.nativeElement['value'] = valid ? value : 0; } /** * Get key's name * @param e */ getName(e): string { if (e.key) { return e.key; } else { // for old browsers if (e.keyCode && String.fromCharCode) { switch (e.keyCode) { case 8: return 'Backspace'; case 9: return 'Tab'; case 27: return 'Escape'; case 37: return 'ArrowLeft'; case 39: return 'ArrowRight'; case 188: return ','; case 190: return '.'; case 109: return '-'; // minus in numbpad case 173: return '-'; // minus in alphabet keyboard in firefox case 189: return '-'; // minus in alphabet keyboard in chrome default: return String.fromCharCode(e.keyCode); } } } }
Usage:
<input NumbersOnly
[allowDecimals]="true"
[allowSign]="true"
type="text">
Solution 4
I would like to build on the answer given by @omeralper , which in my opinion provided a good foundation for a solid solution.
What I am proposing is a simplified and up to date version with the latest web standards. It is important to note that event.keycode is removed from the web standards, and future browser updates might not support it anymore. See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
Furthermore, the method
String.fromCharCode(e.keyCode);
does not guarantee that the keyCode pertaining to the key being pressed by the user maps to the expected letter as identified on the user's keyboard, since different keyboard configurations will result in a particular keycode different characters. Using this will introduce bugs which are difficult to identify, and can easily break the functionality for certain users. Rather I'm proposing the use of event.key, see docs here https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
Furthermore, we only want that the resultant output is a valid decimal. This means that the numbers 1, 11.2, 5000.2341234 should be accepted, but the value 1.1.2 should not be accepted.
Note that in my solution i'm excluding cut, copy and paste functionality since it open windows for bugs, especially when people paste unwanted text in associated fields. That would required a cleanup process on a keyup handler; which isn't the scope of this thread.
Here is the solution i'm proposing.
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[myNumberOnly]'
})
export class NumberOnlyDirective {
// Allow decimal numbers. The \. is only allowed once to occur
private regex: RegExp = new RegExp(/^[0-9]+(\.[0-9]*){0,1}$/g);
// Allow key codes for special events. Reflect :
// Backspace, tab, end, home
private specialKeys: Array<string> = [ 'Backspace', 'Tab', 'End', 'Home' ];
constructor(private el: ElementRef) {
}
@HostListener('keydown', [ '$event' ])
onKeyDown(event: KeyboardEvent) {
// Allow Backspace, tab, end, and home keys
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
// Do not use event.keycode this is deprecated.
// See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
let current: string = this.el.nativeElement.value;
// We need this because the current value on the DOM element
// is not yet updated with the value from this event
let next: string = current.concat(event.key);
if (next && !String(next).match(this.regex)) {
event.preventDefault();
}
}
}
Solution 5
A more concise solution. Try this directive.
Can also be used if you're using ReactiveForms.
export class NumberOnlyDirective {
private el: NgControl;
constructor(private ngControl: NgControl) {
this.el = ngControl;
}
// Listen for the input event to also handle copy and paste.
@HostListener('input', ['$event.target.value'])
onInput(value: string) {
// Use NgControl patchValue to prevent the issue on validation
this.el.control.patchValue(value.replace(/[^0-9]/g, ''));
}
}
The use it on your inputs like this:
<input matInput formControlName="aNumberField" numberOnly>
Related videos on Youtube
Aniruddha Pondhe
UI/UX Ninja & Front End Developer. Enjoy turning complex problems into simple, beautiful and intuitive interface designs. 2.5+ years of professional experience in Application Development Framework (ADF), Frontend Development and Mobile Design. Proficient in HTML, CSS3, JavaScript, jQuery, Bootstrap, Responsive Design and MVC. Pixel-perfect attention-to-detail, highly organized, strong communication skills and a team-player. When I am not coding, you will find me in the kitchen cooking up something new!
Updated on August 12, 2021Comments
-
Aniruddha Pondhe almost 3 years
In Angular 2, how can I mask an input field (textbox) such that it accepts only numbers and not alphabetical characters?
I have the following HTML input:
<input type="text" *ngSwitchDefault class="form-control" (change)="onInputChange()" [(ngModel)]="config.Value" (focus)="handleFocus($event)" (blur)="handleBlur($event)" />
The above input is a generic text input which may either be used as a simple text field or as a numeric field, for example, to show the year.
Using Angular 2, how can I use the same input control and apply some sort of filter/mask on this field, such that it accepts only numbers?
What are the different ways I can achieve this?
Note: I need to achieve this using only textbox and not using input number type.
-
inoabrian over 7 yearsWould you be able to just use the html attribute? type=number
-
Aniruddha Pondhe over 7 years@inoabrian I want to achieve this without using the number type.
-
chandan7 over 7 yearsThis may help you: stackoverflow.com/questions/39799436/…
-
-
Aniruddha Pondhe over 7 yearsI want to achieve this without using the number type.
-
Aniruddha Pondhe over 7 yearsThat's great. Any way I can achieve the same using RegEx patterns?
-
Al-Mothafar about 7 years@Shardul just add
(e.keyCode == 86 && e.ctrlKey === true)
to conditions, copy is working but paste was not working -
Nicolas Forney about 7 yearsSupport for the number type is still quite buggy as described in this answer : stackoverflow.com/a/14995890/1156185
-
user776686 almost 7 yearsThe downside of
type="number"
is that it accepts charactere
as part of scientific notation -
bosari almost 7 yearscould you elaborate on this? what does event.charCode==8 is doing?
-
daniel almost 7 yearsI added
e.keyCode == 109 || e.keyCode ==190
for.
and-
but-
is not accepted? -
Himanshu Modi over 6 yearsit works great .. but it allows non numeric when you paste using mouse.. so probably that mouse event can be handled separately. Thanks omeralper
-
Ender2050 over 6 yearsThis is a really interesting approach. Do you have any suggestions on how to implement copy/paste functionality without resorting to older methods such as (e.keyCode == 67 && e.ctrlKey === true) ??
-
JeanPaul A. over 6 yearsI haven't personally tried this, however you can similarly listen to the copy/paste events that are triggered. They generate a ClipboardEvent ( developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent ) which contains the data that is being copied/pasted. The only drawback is that this is still experimental and supported by the latest browsers only - caniuse.com/#search=paste
-
Carlos Rodriguez over 6 yearsI tried a similar approach but unfortunately this doesn't work for every case. Your "next" variable assumes that the pressed character is going at the end of the currently typed value. This isn't always the case. For example, if someone types in 100, and then decides to make it 1100 by appending a 1 to the front. Your "next" variable will be incorrect (1001).
-
JeanPaul A. over 6 yearsSince the 'next' value is only used to check if the input amount is a valid decimal (and not to set the value), appending it at the end would not change the regex validation.
-
macha devendher over 6 yearsCan you please explain me how did you handle paste ,because it accepting all strings also @Himanshu Modi
-
eatinasandwich over 6 yearsIt's in other answers, but to blind copy-pasters like myself you should use
event.keyCode ? event.keyCode : event.which
to accommodate firefox because keyCode comes through as 0. -
VR1256 over 6 yearsthe solution provided is not working when we do mouse right click copy and then paste
-
AndyTheEntity over 6 yearsWon't
Number.isInteger(Math.floor(control.value))
be always true? I think it should beparseFloat
instead. -
Dilshan Liyanage about 6 yearsThe question is about Angular 2, not angularJS
-
Albert-Jan Verhees about 6 yearsYou can add the Mac Command key by checking
e.metaKey
(e.keyCode == 86 && (e.ctrlKey === true || e.metaKey === true))
-
Satheesh Natarajan about 6 yearsIf start type with char, char will not append but count of the model length takes 1. How to solve that?. Also if element has a max length, then copy and paste mixed content, model count would be max length. For example, max length have 10, then copy and paste 1238261jhgjh12987 to input will append 123816 only but the length of the model would take 10. Any solutions?
-
Satheesh Natarajan about 6 yearsIf start type with char, char will not append but count of the model length takes 1. How to solve that?
-
Lahar Shah about 6 yearswhen do you check for the length, it remains 0 in the directive before and after making changes. If at some point it's one it should come back to 0 quickly.
-
Satheesh Natarajan about 6 yearsNo it's not. Just try to bind numModel.length in template and check the length count
-
Lrodriguez84 about 6 yearsOnly I would like add this line to apply in input control. <input myNumberOnly type="text" id="yourId">
-
Jason Brody almost 6 yearshow to give max length value from input field
-
Jason Brody almost 6 years<input id="COMN" class="wb-e-inp-1__input" type="text" appNumberOnly maxlength="10"/> working
-
Darryn Hosking over 5 yearsThe problem with this approach is that key events do not capture a user pasting or a browser auto filling the input field. So this is a poor solution.
-
Abdul Rehman Sayed over 5 yearsI changed the last line of validatevalue method to prevent adding a zero for invalid paste. if (valid) { this.hostElement.nativeElement['value'] = value;}
-
pblack over 5 yearsAlso IE11 returns the string Decimal for event.key for the decimal key on the numeric keypad
-
Sushmit Sagar about 5 yearscan you please also add drag and drop validation too? Also, I noticed that the input fields value does change to the 0 padded value for leading and trailing decimal separator but the value doesn't update in the two way binding variable. for example: [(NgModel)]="myVariable" , here, if we type .3 in input field the value in text input changes to 0.3 on blur but the value in myVariable still remains '.3' .
-
leox almost 5 yearsThis approach don't support negative numbers.
-
Raja Rama Mohan Thavalam almost 5 yearskeyCode Deprecated developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
-
Rinku Choudhary over 4 yearsyes it is helpful but I want (.)decimal also in my input field
-
ntziolis over 4 yearsWhile this solution works it triggers model change events twice, that said the approach of using regex is the right one, here is a version that does NOT fire model changed events twice: stackblitz.com/edit/…
-
Nate over 4 yearsThis is awesome! @Directive({ selector: "[inputNumericInput]" }) export class NumericInputDirective { @HostListener() }
-
user2367418 about 4 yearsTo ntziolis's comment: So far Ben Gulapa's solution is working for me. But the solution referenced by ntziolis is not. Forgive me if I am wrong, but it seems that the problem with the code at the above link to stackblitz, at least for me, was that the last unwanted character I typed, although it did not show ever in the user interface, somehow got put into the bound variable of my component. Just the last unwanted character.
-
user2367418 about 4 yearsTo continue my comment: Using Angular 7 and an HMTL input text limited to two characters.
-
vinsinraw about 4 yearsWorks well. Only side effect observed in copy paste. It allows copy paste of external non numeric strings. Googled and found a better solution which addresses this @ stackblitz.com/edit/…
-
qleoz12 almost 4 yearsthe problem comes , when you try to have the same behavior in all browser (this case only numbers, I see the input behaves different chrome vs firefox)
-
YuS over 3 yearsPlease use code blocks to format your code snippets.
-
Seyed-Amir-Mehrizi over 3 yearsit is not working. when you start to type, you type characters that it is wrong
-
ProgFroz over 3 yearsUnfortunantely you can trick this solution by spamming circumflex + whatever you want to write.
-
Oleg Bondarenko over 3 yearsDelete and Enter input is missing, but anyway solution is very good
-
Leebeedev over 3 yearsNice, Thank you mate ^_^
-
AsGoodAsItGets about 3 yearsI tried to modify this answer for my needs (no need for multi-line) only to realize that it fails for
input type="number"
because selectionStart/End are not available for it. In fact, there seems to be no way to get the caret position inside a number input :( -
AsGoodAsItGets about 3 yearsThis assumes that the cursor/caret is always at the end of the typed number. If the user wants to type somewhere in between, it fails.
-
Gabriel H about 3 years@SushmitSagar in order to make sure that 2 way data binding works add: @Output() ngModelChange:EventEmitter<any> = new EventEmitter(); to the declarations and then add: this.ngModelChange.emit(valid ? value : 0); as the last line in validateValue method
-
Gabriel H about 3 yearsalso add those lines to the validateValue method: //remove unnececary leading zeros let secondChar=value.charAt(1); if (firstCharacter=='0'&&(secondChar!=''||secondChar!=this.decimalSeparator)) { value=value.substring(1); }
-
Cesar almost 3 yearsThe downside of the type="number" is that it breaks the keyboard and sends the user guessing why the keyboard stopped working. Filtering is very anti user friendly, a better approach is to show an clear error message and keep the input intact. When a user enters a1b2c3 it makes absolutely no sense to give him the result "123"
-
Eliseo almost 3 years