Reputation: 3043
I'm creating dynamic input field it will accept all type values. I need to restrict only numbers to be enter.
template:
<tr *ngFor="let item of rowData">
<ng-container *ngFor="let hitem of columnDefs" >
<td *ngIf="!hitem.dropdown; else selectDrop">
<span *ngIf="hitem.edit;else content">
<div *ngIf="editing">
<input [required]="required" [name]="item[hitem.field]" [(ngModel)]="item[hitem.field]" />
</div>
<div *ngIf="!editing">
{{item[hitem.field]}}
</div>
</span>
<ng-template #content>content here... {{item[hitem.field]}} </ng-template>
</td>
<ng-template #selectDrop>
<td>
<select [(ngModel)]="item[hitem.field]">
<option *ngFor="let item of aplAry">{{item}}</option>
</select>
</td>
</ng-template>
</ng-container>
</tr>
data:
mainHead = [{name:'', colspan:1}, {name:'Deatils', colspan:2}]
columnDefs = [
{headerName: 'Make', field: 'make', edit:true },
{headerName: 'Model', field: 'model', dropdown: true },
{headerName: 'Price', field: 'price', edit:true}
];
aplAry = ['Celica','Mondeo','Boxter'];
rowData = [
{ make: 'Toyota', model: 'Celica', price: 35000 },
{ make: 'Ford', model: 'Mondeo', price: 32000 },
{ make: 'Porsche', model: 'Boxter', price: 72000 }
];
Upvotes: 5
Views: 52872
Reputation: 56
Instead of using keycode to detect the input (whether char or number) its better to use regular expression to match the input type. Below is a directive doing that. The advantage of using regex is that your code becomes flexible. For example if you need to limit number of digits in your decimal number to 7 max & decimal places to 2 then you can simply change the regex to /^[0-9]{1,7}(.[0-9]{0,2}){0,1}$/g
@Directive({
selector: '[allowNumbers]'
})
export class AllowOnlyNumbersDirective {
private decRegex: RegExp = new RegExp(/^[0-9]+(\.[0-9]*){0,1}$/g);
private specialKeys: Array<string> = ['Backspace', 'Tab', 'End', 'Home', 'ArrowLeft', 'ArrowRight', 'Delete', 'Enter', 'Control', 'Cut', 'Copy', 'Paste'];
targetElement: any;
constructor(element: ElementRef) {
this.targetElement = element.nativeElement;
}
@HostListener('paste', ['$event'])
onPaste(event: ClipboardEvent) {
const pastedValue = event.clipboardData.getData('text/plain');
console.log(pastedValue);
if (pastedValue && !String(pastedValue).match(this.decRegex)) {
event.preventDefault(); //prevent the paste from happening
}
}
@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) {
return; //if any of the special keys like backspace, delete, arrow keys etc. are pressed, then return
}
const inputTextBoxValue: string = this.targetElement.value.concat(event.key); //concatenate the previous value with the new typed value
console.log(`event.key: ${event.key}, inputValue: ${inputTextBoxValue}`);
if (inputTextBoxValue && !String(inputTextBoxValue).match(this.decRegex)) {
event.preventDefault(); //if its not a number then prevent the keypress
}
return;
}
}
Usage:
<input allowOnlyNumbers type = "text" id = "someId" formControlName = "someName" />
Upvotes: 0
Reputation: 432
Add validation like this
sufficient_observation_count: new FormControl( 0,[Validators.pattern("^(0|[1-9][0-9]{0,100})$"),Validators.required])
The pattern will check that first digit shoud be whether 0 or from 1 to 9,and then subsequent 100 digits can be from 0 to 9,so it checks for decimals,negative numbers,exponentials etc.
Upvotes: 0
Reputation: 7119
It can be done as follow:
in component.html
<input (keypress)="numberOnly($event)" type="text">
in component.ts
numberOnly(event): boolean {
const charCode = (event.which) ? event.which : event.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57)) {
return false;
}
return true;
}
Upvotes: 0
Reputation: 1
@Directive({
selector: '[cdeCurrencyPipe]',
})
export class CDECurrencyPipe {
private navigationKeys = ['Backspace','Delete','Tab','Escape','Enter','Home','End','ArrowLeft','ArrowRight','Clear','Copy','Paste'];
@Input() OnlyNumber: boolean;
constructor(private control : NgControl,
private elementRef: ElementRef,
private currencyPipe: CurrencyPipe) { }
@HostListener('blur', ['$event.target.value'])
onBlur(raw) {
if(raw && raw.length > 0){
var strVal = raw.replace(/[$,]/g,"");
this.elementRef.nativeElement.value = this.currencyPipe.transform(strVal);
}
}
@HostListener('focus', ['$event.target.value'])
onChange(raw) {
if(raw && raw.length > 0){
var strVal = raw.replace(/[$,]/g,"");
this.control.valueAccessor.writeValue(strVal)
}
}
@HostListener('keydown', ['$event'])
onKeyDown(e: KeyboardEvent) {
if (
// Allow: Delete, Backspace, Tab, Escape, Enter, etc
this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
(e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
(e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
(e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
(e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
(e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
(e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
(e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
(e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
) {
// 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();
}
}
}
Upvotes: 0
Reputation: 8894
You can achieve by using directive.
@Directive({
selector: "input[numbersOnly]"
})
export class NumberDirective {
constructor(private _el: ElementRef) {}
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
if (
// Allow: Delete, Backspace, Tab, Escape, Enter
[46, 8, 9, 27, 13].indexOf(e.keyCode) !== -1 ||
(e.keyCode === 65 && e.ctrlKey === true) || // Allow: Ctrl+A
(e.keyCode === 67 && e.ctrlKey === true) || // Allow: Ctrl+C
(e.keyCode === 86 && e.ctrlKey === true) || // Allow: Ctrl+V
(e.keyCode === 88 && e.ctrlKey === true) || // Allow: Ctrl+X
(e.keyCode === 65 && e.metaKey === true) || // Cmd+A (Mac)
(e.keyCode === 67 && e.metaKey === true) || // Cmd+C (Mac)
(e.keyCode === 86 && e.metaKey === true) || // Cmd+V (Mac)
(e.keyCode === 88 && e.metaKey === true) || // Cmd+X (Mac)
(e.keyCode >= 35 && e.keyCode <= 39) // Home, End, Left, Right
) {
return; // let it happen, don't do anything
}
// 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();
}
}
}
Show the directive in html,
<input [required]="required" [name]="item[hitem.field]" [(ngModel)]="item[hitem.field]" numbersOnly/>
Declare the directive in AppModule
@NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent, HelloComponent,NumberDirective ],
bootstrap: [ AppComponent ]
})
Working Stackblitz
If you like to allow numbers only in price field only:
<input [required]="required" [name]="item[hitem.field]" [(ngModel)]="item[hitem.field]" [numbersOnly]="hitem.headerName!='Make'"/>
and in directive,
export class NumberDirective {
@Input() numbersOnly:boolean;
constructor(private _el: ElementRef) {}
@HostListener("keydown", ["$event"])
onKeyDown(e: KeyboardEvent) {
if(!this.numbersOnly)
return;
....// rest of codes
}
Upvotes: 2
Reputation: 22213
To do it dynamically,
include type in columnDefs
objects:
columnDefs = [
{ headerName: "Make", field: "make", edit: true, type: "text" },
{ headerName: "Model", field: "model", dropdown: true, type: "text" },
{ headerName: "Price", field: "price", edit: true, type: "number" }
];
Template:
<input [required]="required" [type]="hitem.type" [name]="item[hitem.field]" [(ngModel)]="item[hitem.field]" />
Upvotes: 2
Reputation: 9687
You can create a custom directive for only number
.
Stackblitz Demo
app.component.html
<input type="text" appOnlynumber/>
onlynumber.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appOnlynumber]'
})
export class OnlynumberDirective {
private navigationKeys = [
'Backspace',
'Delete',
'Tab',
'Escape',
'Enter',
'Home',
'End',
'ArrowLeft',
'ArrowRight',
'Clear',
'Copy',
'Paste'
];
inputElement: HTMLElement;
constructor(public el: ElementRef) {
this.inputElement = el.nativeElement;
}
@HostListener('keydown', ['$event'])
onKeyDown(e: KeyboardEvent) {
if (
this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
(e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
(e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
(e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
(e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
(e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
(e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
(e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
(e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
) {
// 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();
}
}
@HostListener('paste', ['$event'])
onPaste(event: ClipboardEvent) {
event.preventDefault();
const pastedInput: string = event.clipboardData
.getData('text/plain')
.replace(/\D/g, ''); // get a digit-only string
document.execCommand('insertText', false, pastedInput);
}
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
event.preventDefault();
const textData = event.dataTransfer.getData('text').replace(/\D/g, '');
this.inputElement.focus();
document.execCommand('insertText', false, textData);
}
}
Upvotes: 20
Reputation: 8650
Here's a simpler approach using a directive.
export class NumbersOnlyDirective {
@Input('field') field;
constructor(private ngControl: NgControl) { }
@HostListener('input', ['$event']) onInput(event): void {
if (this.field === 'price') {
const value = event.target.value;
this.ngControl.control.setValue(parseFloat(value) || 0);
if (value.slice(-1) === '.' && !value.slice(0, -1).includes('.')) {
event.target.value = value;
}
}
}
}
This directive will only allow decimal numbers to be entered. parseFloat
removes the alphabets and other special characters. I have used || 0
as a fallback in case the field is emptied but if you don't want anything to display, simply use || ''
instead. The if
condition ensures that only one decimal point can be entered unlike when you use type="number"
(type="number"
will also change the ngModel
to a string). The condition is placed after we update the control
value so that if the last entered value is a .
, the ngModel
value will not include the .
while the view will contain it.
Then use this directive in your template like below and pass the field
value so that the this logic will only apply to the price
field.
<input [required]="required" numbersOnly [field]="hitem.field" [name]="item[hitem.field]" [(ngModel)]="item[hitem.field]" />
Here is a working example on StackBlitz.
Upvotes: 2
Reputation: 825
Add type="number"
in your input element
<input [required]="required" [name]="item[hitem.field]" [(ngModel)]="item[hitem.field]" type="number" />
If you want to change input type dynamically then [type]="type"
& in your .ts file set type="text | number | email | tel, etc"
Upvotes: 1