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.
This question is related to
html
angular
input
angularjs-directive
I saw a lot of comments about handling copy/pasting.
To piggy back off of @omeralper answer, you can add a paste event handler to the onlyNumber directive to handle copy/pasting:
@HostListener('paste', ['$event']) onPaste(event) {
// Don't allow pasted text that contains non-numerics
var pastedText = (event.originalEvent || event).clipboardData.getData('text/plain');
if (pastedText) {
var regEx = new RegExp('^[0-9]*$');
if (!regEx.test(pastedText)) {
event.preventDefault();
}
}
This will only allow content to be copy and pasted into the textbox ONLY if it is a number. That's the simplest solution. Changing the content of the clipboard to remove non-numerics is a lot more complicated and might not be worth it.
To get pasted text from IE you can use the following:
window.clipboardData.getData('Text');
Just Create a directive and add below hostlistener:
@HostListener('input', ['$event'])
onInput(event: Event) {
this.elementRef.nativeElement.value = (<HTMLInputElement>event.currentTarget).value.replace(/[^0-9]/g, '');
}
Replace invalid text with empty. All keys and key combinations will now work across all browsers till IE9.
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;
}
}
Below is my angular code that allows the only number to enter and only paste number, not text.
<input id="pId" maxlength="8" minlength="8" type="text" [(ngModel)]="no" formControlName="prefmeno" name="no" class="form-control">
And in ts file added in ngOnIt.
ngOnInit() {
setTimeout(() => {
jQuery('#pId').on('paste keyup', function(e){
jQuery(this).val(document.getElementById('pId').value.replace(/[^\d]/g, ''));
});
}, 2000);
}
I used setTimeout for waiting time to load DOM. And used jquery with javascript to perform this task. 'Paste' and 'keyup' are used to trigger paste and enter in the field.
from @omeralper 's answer. I change a little bit that won't accept period ascii (keycode 110,190). and use let ch = (e.key); to compare with regular expression when you change language (such as Thai or Japanese language) it won't accept character of those language
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) {
// console.log(event, this.OnlyNumber);
if ([46, 8, 9, 27, 13].indexOf(e.keyCode) !== -1) {
return;
}
let ch = (e.key);
let regEx = new RegExp(this.regexStr);
if(regEx.test(ch))
return;
else
e.preventDefault();
}
}
}
hope this help :)
fromCharCode returns 'a' when pressing on the numpad '1' so this methoid should be avoided
(admin: could not comment as usual)
You could also create a directive which implements the ControlValueAccessor Interface (https://angular.io/api/forms/ControlValueAccessor).
See working example here: https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers
You can listen to the 'input' event and there is no need to check for keycodes. It supports copy & paste and integrates nicely with the Angular Forms API due to the ControlValueAccessor Interface.
Directive:
@Directive({
...
selector: '[onlyNumber]'
})
export class OnlyNumberDirective implements ControlValueAccessor {
private onChange: (val: string) => void;
...
private value: string;
constructor(
private elementRef: ElementRef,
private renderer: Renderer2
) {
}
...
@HostListener('input', ['$event.target.value'])
onInputChange(value: string) {
const filteredValue: string = filterValue(value);
this.updateTextInput(filteredValue, this.value !== filteredValue);
}
private updateTextInput(value, propagateChange) {
this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
if (propagateChange) {
this.onChange(value);
}
this.value = value;
}
// ControlValueAccessor Interface
...
registerOnChange(fn: any): void {
this.onChange = fn;
}
writeValue(value: string): void {
value = value ? String(value) : '';
this.updateTextInput(value, false);
}
}
function filterValue(value): string {
return value.replace(/[^0-9]*/g, '');
}
Usage:
<input name="number" type="text" onlyNumber [(ngModel)]="someNumber">
Well Thanks to JeanPaul A. and rdanielmurphy. I had made my own Custom directive for limiting input field to number only. Also added the max and min input attributes. Will work in angular 7 also.
Angular
import { Directive, ElementRef, Input, HostListener } from '@angular/core';
@Directive({
selector: '[appNumberOnly]'
})
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) { }
@Input() maxlength: number;
@Input() min: number;
@Input() max: number;
@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
const 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
const next: string = current.concat(event.key);
if (next && !String(next).match(this.regex) || (this.maxlength && next.length > this.maxlength) ||
(this.min && +next < this.min) ||
(this.max && +next >= this.max)) {
event.preventDefault();
}
}
@HostListener('paste', ['$event']) onPaste(event) {
// Don't allow pasted text that contains non-numerics
const pastedText = (event.originalEvent || event).clipboardData.getData('text/plain');
if (pastedText) {
const regEx = new RegExp('^[0-9]*$');
if (!regEx.test(pastedText) || (this.maxlength && pastedText.length > this.maxlength) ||
(this.min && +pastedText < this.min) ||
(this.max && +pastedText >= this.max)) {
event.preventDefault();
}
}
}
}
HTML
<input type="text" class="text-area" [(ngModel)]="itemName" maxlength="3" appNumberOnly />
If you use primeng and Angular 6 or above there is the p-inputMask component. It prevents alpha typing AND negative values https://www.primefaces.org/primeng/#/inputmask
In order to accomplish this, I bound a function to the onInput method like this:
(input)="stripText(infoForm.get('uin'))
Here is the example inside my form:
<form [formGroup]="infoForm" (submit)="next()" class="ui form">
<input type="text" formControlName="uin" name="uin" id="uin" (input)="stripText(infoForm.get('uin'))" required/>
</form>
Then I added the following function to my component:
stripText(control: FormControl) {
control.setValue(control.value.replace(/[^0-9]/g, ''));
}
This regex /[^0-9]/g
searches for anything that is not a number and using .replace
I set it to be replaced by nothing. So when a user tries to type in a character that is not a number (in this case a character that is not zero through nine), it appears as if nothing happens in the text box.
Would not be simple enough just to write
onlyNumbers(event) {
if(isNaN(event.target.value * 1)) {
console.log("Not a number")
} else {
console.log("Number")
}
}
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();
}
}
}
You can create this Validator and import it in your component.
Basically validates the form input string:
To implement it in your project:
import in your component
import { NumberValidator } from '../../validators/number.validator';
inputNumber: ['', [NumberValidator.isInteger]],
(change)="deleteCharIfInvalid()"
to the input, if form.get('inputNumber').hasError('isInteger')
is true
, delete the last char inserted.// FILE: src/app/validators/number.validator.ts
import { FormControl } from '@angular/forms';
export interface ValidationResult {
[key: string]: boolean;
}
export class NumberValidator {
public static isInteger(control: FormControl): ValidationResult {
// check if string has a dot
let hasDot:boolean = control.value.indexOf('.') >= 0 ? true : false;
// convert string to number
let number:number = Math.floor(control.value);
// get result of isInteger()
let integer:boolean = Number.isInteger(number);
// validate conditions
let valid:boolean = !hasDot && integer && number>0;
console.log('isInteger > valid', hasDot, number, valid);
if (!valid) {
return { isInteger: true };
}
return null;
}
}
With support for sanitizing pasted content:
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[NumbersOnly]'
})
export class NumbersOnlyDirective {
DIGITS_REGEXP = new RegExp(/\D/g);
constructor(private el: ElementRef) {
// Sanatize clipboard by removing any non-numeric input after pasting
this.el.nativeElement.onpaste = (e:any) => {
e.preventDefault();
let text;
let clp = (e.originalEvent || e).clipboardData;
if (clp === undefined || clp === null) {
text = (<any>window).clipboardData.getData('text') || '';
if (text !== '') {
text = text.replace(this.DIGITS_REGEXP, '');
if (window.getSelection) {
let newNode = document.createElement('span');
newNode.innerHTML = text;
window.getSelection().getRangeAt(0).insertNode(newNode);
} else {
(<any>window).selection.createRange().pasteHTML(text);
}
}
} else {
text = clp.getData('text/plain') || '';
if (text !== '') {
text = text.replace(this.DIGITS_REGEXP, '');
document.execCommand('insertText', false, text);
}
}
};
}
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent> event;
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();
}
}
}
You can do this easily using a mask:
<input type='text' mask="99" formControlName="percentage" placeholder="0">
99 - optional 2 digits
Don't forget to import NgxMaskModule in your module:
imports: [
NgxMaskModule.forRoot(),
]
A modern approach for the best answer (without deprecated e.keyCode):
@HostListener('keydown', ['$event']) onKeyDown(event) {
let e = <KeyboardEvent> event;
if (['Delete', 'Backspace', 'Tab', 'Escape', 'Enter', 'NumLock', 'ArrowLeft', 'ArrowRight', 'End', 'Home', '.'].indexOf(e.key) !== -1 ||
// Allow: Ctrl+A
(e.key === 'a' && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+C
(e.key === 'c' && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+V
(e.key === 'v' && (e.ctrlKey || e.metaKey)) ||
// Allow: Ctrl+X
(e.key === 'x' && (e.ctrlKey || e.metaKey))) {
// let it happen, don't do anything
return;
}
// Ensure that it is a number and stop the keypress
if ((e.shiftKey || ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].indexOf(e.key) === -1)) {
e.preventDefault();
}
}
Use pattern
attribute for input like below:
<input type="text" pattern="[0-9]+" >
Here is easy one: Simple directive
On keydown event it checks the length of a key is one and key is not a number to preventDefault()
and it won't renders that char.
import {Directive, ElementRef, HostListener} from '@angular/core';
@Directive({
selector: '[numbersOnly]'
})
export class NumbersOnlyDirective {
@HostListener('keydown', ['$event'])
keyDownEvent(event: KeyboardEvent) {
if (event.key.length === 1 && (event.which < 48 || event.which > 57)) {
event.preventDefault();
}
}
}
HTML:
<input type="text" [(ngModel)]="numModel" numbersOnly />
Limitations: It will allow pasting using a mouse that way will accept other char. To avoid that you can pass the model as input to the directive and ngOnChage
to that model change value to only numbers:
Like below:
EDIT: Added Code to detect change in Model and update the input's value
import {Directive, ElementRef, HostListener, Input, OnChanges} from '@angular/core';
@Directive({
selector: '[numbersOnly]'
})
export class NumbersOnlyDirective implements OnChanges {
@Input() numbersOnly: any;
constructor(private el: ElementRef) {}
@HostListener('keydown', ['$event'])
keyDownEvent(event: KeyboardEvent) {
// Add other conditions if need to allow ctr+c || ctr+v
if (event.key.length === 1 && (event.which < 48 || event.which > 57)) {
event.preventDefault();
}
}
ngOnChanges(changes) {
if (changes.numbersOnly) {
this.el.nativeElement.value = this.el.nativeElement.value.replace(/[^0-9]/g, '');
}
}
}
HTML:
<input type="text" [(ngModel)]="numModel" [numbersOnly]="numModel" />
You could use regex:
<input type="text" (keypress)="numericOnly($event)">
numericOnly(event): boolean {
let patt = /^([0-9])$/;
let result = patt.test(event.key);
return result;
}
Use directive to restrict the user to enter only numbers in the following way:
.directive('onlyNumber', function () {
var regExp = /^[0-9]*$/;
return {
require: '?ngModel',
restrict: 'A',
priority: 1,
link: function (scope, elm, attrs, ctrl) {
ctrl.$validators.onlyNumber= function (modalValue) {
return ctrl.$isEmpty(modalValue) || regExp.test(modalValue);
};
}
};
})
In HTML:
<input id="txtRollNumber" type="text" name="rollNumber" placeholder="Enter roll number*" ng-model="rollNumber" class="form-control" maxlength="100" required only-number />
Angular2:
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 need to write the directive name in your input as an attribute.
<input OnlyNumber="true" />
<input type="text" (keypress)="keyPress($event)">
keyPress(event: any) {
const pattern = /[0-9\+\-\ ]/;
let inputChar = String.fromCharCode(event.charCode);
if (event.keyCode != 8 && !pattern.test(inputChar)) {
event.preventDefault();
}
}
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();
}
}
}
Casting because it works also with leading 0 like 00345
@Directive({
selector: '[appOnlyDigits]'
})
export class AppOnlyDigitsDirective {
@HostListener('input', ['$event'])
onKeyDown(ev: KeyboardEvent) {
const input = ev.target as HTMLInputElement;
input.value = String(input.value.replace(/\D+/g, ''));
}
}
import {Directive, ElementRef, HostListener, Output, EventEmitter} from '@angular/core';
//only-digits
@Directive({
selector: '[only-digits]'
})
export class OnlyDigits {
constructor(public el: ElementRef) {
this.el.nativeElement.onkeypress = (evt) => {
if (evt.which < 48 || evt.which > 57) {
evt.preventDefault();
}
};
}
}
Just use type number on your text like below:
<input type="number" class="form-control" matInput name="value" placeholder="xxx" (change)="xxx()" formControlName="value">
HTML:
<input type="text" maxlength="5"(focusout)="onTextboxFocusOutDecimalPercentageValidate($event)"
(input)="onTextboxChangeDecimalPercentageValidate($event)">
TS:
onTextboxChangeDecimalPercentageValidate(event: Event) {
var inputData = (<HTMLInputElement>event.target).value;
//replace more than one dot
var extractedFte = inputData.replace(/[^0-9.]/g, '').replace('.', 'x')
.replace(/\./g, '').replace('x', '.');
//Extract Decimal Values
extractedFte = extractedFte.replace(/^(\d+.?\d{0,2})\d*$/, "$1");
//Reasign to same control
(<HTMLInputElement>event.target).value = extractedFte;
if (extractedFte != '' && Number(extractedFte) >= 100) {
(<HTMLInputElement>event.target).value = '100'; extractedFte = '100';
}
// if (Number(extractedFte) == 0) {
// (<HTMLInputElement>event.target).value = ''; extractedFte = '';
// }
}
onTextboxFocusOutDecimalPercentageValidate(event: Event) {
var inputData = (<HTMLInputElement>event.target).value;
//replace more than one dot
var extractedFte = inputData.replace(/[^0-9.]/g, '').replace('.', 'x')
.replace(/\./g, '').replace('x', '.');
//Extract Decimal Values
extractedFte = extractedFte.replace(/^(\d+.?\d{0,2})\d*$/, "$1");
//Reasign to same control
(<HTMLInputElement>event.target).value = extractedFte;
if (extractedFte != '' && Number(extractedFte) >= 100) {
(<HTMLInputElement>event.target).value = '100'; extractedFte = '100';
}
if (Number(extractedFte) == 0) {
(<HTMLInputElement>event.target).value = ''; extractedFte = '';
}
}
I know this is an old question, but since this is a common funcionality, I want to share the modifications I've made:
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">
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>
you can achive it like this
<input type="text" pInputText (keypress)="onlyNumberKey($event)" maxlength="3">
onlyNumberKey(event) {
return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57;
}
//for Decimal you can use this as
onlyDecimalNumberKey(event) {
let charCode = (event.which) ? event.which : event.keyCode;
if (charCode != 46 && charCode > 31
&& (charCode < 48 || charCode > 57))
return false;
return true;
}
hope this will help you.
Here is small directive which use arbitrary regexp and block user to type invalid value
import {Directive, HostListener, Input} from '@angular/core';_x000D_
_x000D_
@Directive({selector: '[allowedRegExp]'})_x000D_
export class AllowedRegExpDirective {_x000D_
_x000D_
@Input() allowedRegExp: string;_x000D_
_x000D_
@HostListener('keydown', ['$event']) onKeyDown(event: any) {_x000D_
// case: selected text (by mouse) - replace it_x000D_
let s= event.target.selectionStart;_x000D_
let e= event.target.selectionEnd;_x000D_
let k= event.target.value + event.key;_x000D_
_x000D_
if(s!=e) {_x000D_
k= event.target.value_x000D_
k= k.slice(0,s) + event.key + k.slice(e,k.length);_x000D_
}_x000D_
_x000D_
// case: special characters (ignore)_x000D_
if(['ArrowLeft','ArrowRight','ArrowUp','ArroDown','Backspace','Tab','Alt'_x000D_
'Shift','Control','Enter','Delete','Meta'].includes(event.key)) return;_x000D_
_x000D_
// case: normal situation - chceck regexp_x000D_
let re = new RegExp(this.allowedRegExp);_x000D_
_x000D_
if(!re.test(k)) event.preventDefault();_x000D_
}_x000D_
}
_x000D_
To mask only numbers use
<input [allowedRegExp]="'^[0-9]*$'" type="text" ... >
<input type="number" min="0" oninput="this.value = Math.abs(this.value)">
I have made some modifications in the above directive and implemented min, max, maxlength.
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[numberOnly]'
})
export class NumbersOnlyDirective {
private regex: RegExp = new RegExp(/[0-9]/g);
// Allow key codes for special events. Reflect :
private specialKeys: Array<number> = [46, 8, 9, 27, 13, 110, 190, 35, 36, 37, 39];
// Backspace, tab, end, home
@Input() maxlength: number;
@Input() min: number;
@Input() max: number;
constructor(private el: ElementRef) {
}
@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
e = <KeyboardEvent>event;
if ((
(this.specialKeys.indexOf(event.which) > -1) ||
// to allow backspace, enter, escape, arrows
(e.which == 65 && e.ctrlKey == true) ||
// Allow: Ctrl+C
(e.which == 67 && e.ctrlKey == true) ||
// Allow: Ctrl+X
(e.which == 88 && e.ctrlKey == true))) {
return;
} else if (// to allow numbers
(e.which >= 48 && e.which <= 57) ||
// to allow numpad number
(event.which >= 96 && event.which <= 105)) { }
else {
event.preventDefault();
}
let current: string = this.el.nativeElement.value;
let next: string = current.concat(event.key);
if ((next && !String(next).match(this.regex)) ||
(this.maxlength && next.length > this.maxlength) ||
(this.min && +next < this.min) ||
(this.max && +next >= this.max)) {
event.preventDefault();
}
}
}
<input oninput="this.value=this.value.replace(/[^0-9]/g,'')"
or: 2. in The HTML File :
<input [(ngModel)]="data" (keypress)="stripText($event)"
class="form-control">
in The ts File:
stripText(event) {
const seperator = '^([0-9])';
const maskSeperator = new RegExp(seperator , 'g');
let result =maskSeperator.test(event.key); return result; }
This 2 solution works
You need to use type="number" instead text. You can also specify max and min numbers
<input type="number" name="quantity" min="1" max="5">
Pattern for the Valid Mobile number pattern('^((\+91-?)|0)?[0-9]{10}$')
Pattern for accept only number from text box pattern('[0-9]*')
patter for accept only number with specific number e.g: Pincode. pattern('^[0-9]{5}$')
Just use HTML5, input type=”number”
I know this is has a lot of answers, but I needed to handle the following (which none of the answers seemed to fully support):
The solution allows me to define a textarea like this:
<textarea class="form-control" [(ngModel)]="this.myModelVariable"
appOnlyNumbers [allowNegative]="true" [allowMultiLine]="true"
[allowDecimal]="true" [maxLength]="10"
placeholder="Enter values (one per line)"></textarea>
Or if I just want positive integers
<textarea class="form-control" [(ngModel)]="this.myModelVariable"
appOnlyNumbers [allowMultiLine]="true" [maxLength]="9"
placeholder="Enter values (one per line)"></textarea>
Here is my directive:
import { Directive, HostListener, Input, ElementRef } from '@angular/core';
@Directive({
selector: '[appOnlyNumbers]'
})
export class OnlyNumbersDirective {
constructor(private el: ElementRef) { }
@Input() allowMultiLine: boolean = false;
@Input() allowNegative: boolean = false;
@Input() allowDecimal: boolean = false;
@Input() maxLength: number = 0;
regex: RegExp;
@HostListener('keypress', ['$event'])
onKeyPress(event: KeyboardEvent) {
this.validate(event, event.key === 'Enter' ? '\n' : event.key);
}
@HostListener('paste', ['$event'])
onPaste(event: Event) {
const pastedText = (<any>window).clipboardData && (<any>window).clipboardData.getData('Text') // If IE, use window
|| <ClipboardEvent>event && (<ClipboardEvent>event).clipboardData.getData('text/plain'); // Non-IE browsers
this.validate(event, pastedText);
}
@HostListener('cut', ['$event'])
onCut(event: Event) {
this.validate(event, '');
}
validate(event: Event, text: string) {
const txtInput = this.el.nativeElement;
const newValue = (txtInput.value.substring(0, txtInput.selectionStart)
+ text + txtInput.value.substring(txtInput.selectionEnd));
if (!this.regex) {
this.regex = <RegExp>eval('/^'
+ (this.allowNegative ? '-?' : '')
+ (this.allowDecimal ? '((\\d+\\.?)|(\\.?))\\d*' : '\\d*')
+ '$/g');
}
var lines = this.allowMultiLine ? newValue.split('\n') : [newValue];
for (let line of lines) {
let lineText = line.replace('\r', '');
if (this.maxLength && lineText.length > this.maxLength || !lineText.match(this.regex)) {
event.preventDefault();
return;
}
}
}
}
Source: Stackoverflow.com