49

Is it possible to implement an input that allows to type only numbers inside without manual handling of event.target.value?

In React, it is possible to define value property and afterwards input change will be basically bound to the value (not possible to modify it without value change). See example. And it works just fine without any efforts.

In Angular 2 it is possible to define [value], but it will just set the value initially, and afterwards input is not prevented from the modifications.

I was playing around with ngModel and [value] / (input), see example.

But in both implementation there is essential problem:

  1. when you type 10 (model value is 10; input value is 10) - correct
  2. when you type 10d afterwards (model value is 10 - not modified, all non-digits has been removed; input value is 10d) - incorrect, because the model value is the same as before
  3. when you type 10d3 - (model value is 103; input value is 103) - correct

How to do that simple (from the first glance) component, without manually handling event.target.value?...

UPDATE I am not looking for native HTML5 input[number] element here. Numbers input here is just for the example - there could be way more tasks when i need to restrict input text.

Moreover, input[number] is 1) not restricting me from typing 10ddd and 2) (less important) contains arrows that i do not need.

And the problem here is to prevent user from typing something beyond the restricted values, instead of allow to input anything and validate it afterwards

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
ValeriiVasin
  • 8,628
  • 11
  • 58
  • 78
  • 6
    I hate that we always have to have this discussion. At some point we need a standard input that does this out the box. It should accept how many digits, not allow you to enter characters, negative or not. I know there is a HTML 5 number thingy, but we have not reached the nirvana. I grow tired of having to write this code for every framework, etc.. – Maccurt Jun 23 '17 at 15:37
  • This might be solved with the answer given in: https://stackoverflow.com/questions/41465542/angular2-input-field-to-accept-only-numbers – Ulises Moreno Apr 04 '18 at 16:08
  • For the love of god **don't do this** unless you hate your users. **Always** allow free input and verify/cleanup later. You, SO user, will likely create an inaccessible field if you do this. Is `tab` a number key? How about `ctrl-v`? No? Oops, the user is now stuck in your field because you just prevented everything. — Instead, make it easy by setting the right input type and then set a `pattern` attribute to validate it, and that's it. – fregante Aug 26 '21 at 15:51

19 Answers19

45

In component.ts add this function

_keyUp(event: any) {
    const pattern = /[0-9\+\-\ ]/;
    let inputChar = String.fromCharCode(event.key);

    if (!pattern.test(inputChar)) {
      // invalid character, prevent input
      event.preventDefault();
    }
}

In your template use the following

<input(keyup)="_keyUp($event)">

This will catch the input before angular2 catches the event.

weizong song
  • 515
  • 4
  • 9
39

After did lot of research finally I create a function which full fill the requirement. The function which I created restrict all special character and allow only alphabets and number.. and that function works fine for both either you did copy paste and typing both. Hope it works :)

 public inputValidator(event: any) {
    //console.log(event.target.value);
    const pattern = /^[a-zA-Z0-9]*$/;   
    //let inputChar = String.fromCharCode(event.charCode)
    if (!pattern.test(event.target.value)) {
      event.target.value = event.target.value.replace(/[^a-zA-Z0-9]/g, "");
      // invalid character, prevent input

    }
  }

     <input type="text" [(ngModel)]="abc.abc" (input)="inputValidator($event)" />

How you use -
1) Add above method in your class component of ts file.
2) Call method inputValidator($event) on input event..

Abhijeet
  • 799
  • 6
  • 13
  • Exactly what I needed. Thanks! – Just Shadow Jun 14 '19 at 13:11
  • I invested a lot of time using this solution. It *seemed* to work. But I noticed with Angular 7 with a two character limit on an HTML input text, that although the unwanted characters did not show in the text box, the last unwanted character showed up in the bound variable! I then tried the solution of Ben Gulapa at this link and it worked: https://stackoverflow.com/questions/41465542/angular2-input-field-to-accept-only-numbers – user2367418 Feb 22 '20 at 02:30
  • But I do appreciate the information from Abhijeet regardless. I did learn more about regular expressions from this and applied that to the solution of Ben Gulapa and modified it to pass in the regular expression as an attribute of the directive. – user2367418 Feb 22 '20 at 03:53
  • Thanks, setting the `event.target.value` was what I was missing from mine. – QTom Nov 28 '22 at 14:53
18

The inputmask plugin does the best job of this. Its extremely flexible in that you can supply whatever regex you like to restrict input. It also does not require JQuery.

Step 1: Install the plugin:

npm install --save inputmask

Step2: create a directive to wrap the input mask:

import {Directive, ElementRef, Input} from '@angular/core';
import * as Inputmask from 'inputmask';


@Directive({
  selector: '[app-restrict-input]',
})
export class RestrictInputDirective {

  // map of some of the regex strings I'm using (TODO: add your own)
  private regexMap = {
    integer: '^[0-9]*$',
    float: '^[+-]?([0-9]*[.])?[0-9]+$',
    words: '([A-z]*\\s)*',
    point25: '^\-?[0-9]*(?:\\.25|\\.50|\\.75|)$'
  };

  constructor(private el: ElementRef) {}

  @Input('app-restrict-input')
  public set defineInputType(type: string) {
    Inputmask({regex: this.regexMap[type], placeholder: ''})
      .mask(this.el.nativeElement);
  }

}

Step 3:

<input type="text" app-restrict-input="integer">

Check out their github docs for more information.

Stephen Paul
  • 37,253
  • 15
  • 92
  • 74
  • ERROR in /Users/xxx/Work/dashboard/dashboard/lib/utils/restrict-input.directive.ts (41,46): Property 'includes' does not exist on type 'string[]'. – BBaysinger Mar 31 '17 at 22:52
  • 2
    @BBaysinger: A yes I should probably modify my code to be more backwards compatible. That is an es2016 feature. You can either use a polyfill Array.includes or change that code as follows: Object.keys(this.patterns).indexOf(type) !== -1 ? type : this.patterns[type]; – Stephen Paul Apr 01 '17 at 05:35
  • 2
    Note: this won't work on an input with type="number" – Zymotik Aug 22 '17 at 11:57
  • It is allowing multiple decimal places.! I only want single decimal place – Renil Babu Sep 28 '18 at 04:53
8

Tested Answer By me:

form.html

<input type="text" (keypress)="restrictNumeric($event)">

form.component.ts:

public restrictNumeric(e) {
  let input;
  if (e.metaKey || e.ctrlKey) {
    return true;
  }
  if (e.which === 32) {
   return false;
  }
  if (e.which === 0) {
   return true;
  }
  if (e.which < 33) {
    return true;
  }
  input = String.fromCharCode(e.which);
  return !!/[\d\s]/.test(input);
 }
AT82
  • 71,416
  • 24
  • 140
  • 167
RohanArihant
  • 2,560
  • 4
  • 19
  • 29
  • how to fix if some one want copy paste with your solution ... your solution is fine but I also want that it restrict during copy paste too – Abhijeet Feb 20 '18 at 13:37
6

You can use the HTML5 input of type number

It does not accept any characters in its declaration

<input type="number" [(model)]='myvar' min=0 max=100 step=5 />

Here is an example of its usage with angular 2 [(model)]

http://www.webpackbin.com/VJNUNF0M-

eltonkamami
  • 5,134
  • 1
  • 22
  • 30
5

In HTML in <input> field write: (keypress)="onlyNumberKey($event)"

and in ts file write:

onlyNumberKey(event) {
    return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57;
}
Tasos K.
  • 7,979
  • 7
  • 39
  • 63
Mohit Sahore
  • 141
  • 2
  • 4
3

A few of the answers did not work for me so I took the best bits from some of the answers (thanks guys) and created an Angular 5 Directive that should do the job (and more) for you. It maybe not perfect but it offers flexibility.

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

@Directive({
selector: '[appInputMask]'
})
export class InputMaskDirective {
@Input('appInputMask') inputType: string;

showMsg = false;
pattern: RegExp;

private regexMap = { // add your own
integer: /^[0-9 ]*$/g,
float: /^[+-]?([0-9]*[.])?[0-9]+$/g,
words: /([A-z]*\\s)*/g,
point25: /^\-?[0-9]*(?:\\.25|\\.50|\\.75|)$/g,
badBoys: /^[^{}*+£$%\\^-_]+$/g
};

constructor(public el: ElementRef, public renderer: Renderer2) { };

@HostListener('keypress', ['$event']) onInput(e) {
this.pattern = this.regexMap[this.inputType]
const inputChar = e.key;
this.pattern.lastIndex = 0; // dont know why but had to add this

if (this.pattern.test(inputChar)) {
   // success
  this.renderer.setStyle(this.el.nativeElement, 'color', 'green'); 
  this.badBoyAlert('black');
} else {

  this.badBoyAlert('black');
   //do something her to indicate invalid character
  this.renderer.setStyle(this.el.nativeElement, 'color', 'red');
  e.preventDefault();

}

  }
  badBoyAlert(color: string) {
    setTimeout(() => {
      this.showMsg = true;
      this.renderer.setStyle(this.el.nativeElement, 'color', color);
    }, 2000)
  }

  }

HTML <input class="form-control" appInputMask="badBoys">

SkyReel
  • 160
  • 10
2
<input type="number" onkeypress="return event.charCode >= 48 && event.charCode <= 57" ondragstart="return false;" ondrop="return false;"> 

Input filed only accept numbers, But it's temporary fix only.

Sujith S
  • 555
  • 5
  • 8
1

I think this will solve your problem. I created one directive which filters input from the user and restricts number or text which you want.

This solution is for up to Ionic-3 and Angular-4 users.

import { Directive, HostListener, Input } from '@angular/core';
import { Platform } from 'ionic-angular';

/**
 * Generated class for the AlphabateInputDirective directive.
 *
 * See https://angular.io/api/core/Directive for more info on Angular
 * Directives.
 */
@Directive({
  selector: '[keyboard-input-handler]' // Attribute selector
})
export class IonicKeyboardInputHandler {

  @Input("type") inputType: string;

  isNumeric: boolean = true;
  str: string = "";
  arr: any = [];  

  constructor(
    public platForm: Platform
  ) {
    console.log('Hello IonicKeyboardInputHandler Directive');
  }


  @HostListener('keyup', ['$event']) onInputStart(e) {   

    this.str = e.target.value + '';

    this.arr = this.str.split('');

    this.isNumeric = this.inputType == "number" ? true : false; 

    if(e.target.value.split('.').length === 2){
      return false;
    }    

    if(this.isNumeric){
      e.target.value = parseInt(this.arr.filter( c => isFinite(c)).join(''));
    }      
    else
      e.target.value = this.arr.filter( c => !isFinite(c)).join('');        

    return true;

  }


}
Axel
  • 3,331
  • 11
  • 35
  • 58
1

In html:

 <input (keypress)="onlyNumber(event)"/>

In Component:

onlyNumber(evt) {
    evt = (evt) ? evt : window.event;
    var charCode = (evt.which) ? evt.which : evt.keyCode;
    if (charCode > 31 && (charCode < 48 || charCode > 57)) {
        return false;
    }
    return true;
}
Pradeep B P
  • 396
  • 4
  • 8
0

I think a custom ControlValueAccessor is the best option.

Not tested but as far as I remember, this should work:

<input [(ngModel)]="value" pattern="[0-9]">

hgoebl
  • 12,637
  • 9
  • 49
  • 72
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    My answer is explicitely **not** about `input type="number"`. input:nubmer doesn's support `pattern`. For full flexibility you can provide a custom `ControlValueAccessor`. There are lots of questions with examples from Thierry Templier here on SO about `ControlValueAccessor`. – Günter Zöchbauer May 25 '16 at 13:21
  • Sorry, copy-paste :) Thank you for the response! Unfortunately, your snippet is not working... Here is the plnkr: http://plnkr.co/edit/CZC9Vf6FxmsOhSN0PL0s?p=preview i created. `ControlValueAccessor` sounds interesting to look into :) Quickly checked few examples - looks like over complication for such simple task... – ValeriiVasin May 25 '16 at 14:11
  • Sorry, `pattern` is also just a validation rule that only provides validation errors but doesn't restrict input to specific chars. I guess `ControlValueAccessor` is the way to go and they are not very complicated. Maybe a bit complicated at first to understand but they have just two methods (for read and write). – Günter Zöchbauer May 25 '16 at 15:24
0

Anoher one

<form [formGroup]="myForm" novalidate>
    <input type="text" class="form-control" id="data" name="data"
           formControlName="input3" #item (input)="change(item.value)">
</form>
{{myForm.value |json}}

change(value:string)
  {
    let lastchar = value.substr(value.length - 1);
    if (!(new RegExp('[0-9]').test(lastchar)))
    {
      value=value.substr(0,value.length-1);
      this.myForm.controls["input3"].setValue(value);

    }
  }

if you use from driven template

  <input type="text" class="form-control" id="data" name="data"
       [(ngModel)]="data" #item (input)="change(item)">
   {{data}}

change(item:any)
  {
    let value=item.value;
    let lastchar = value.substr(value.length - 1);
    if (!(new RegExp('[0-9]').test(lastchar)))
    {
      value=value.substr(0,value.length-1);
      item.value=this.data=value;
    }
  }

Update As @BikashBishwokarma comment, this not work if you insert a character in middle. We can change the function by some like

  change(item:any)
  {
    let value=item.value;
    let pos=item.selectionStart;
    if (!(new RegExp('^[0-9]+$').test(value)))
    {
      item.value=this.data=value.replace(/[^0-9]+/g, '');
      item.selectionStart = item.selectionEnd = pos-1;
    }
  }

See, how mainten the cursor position

Eliseo
  • 50,109
  • 4
  • 29
  • 67
0

Below is working solution using NgModel

Add variable

 public Phone:string;

In html add

      <input class="input-width" [(ngModel)]="Phone" (keyup)="keyUpEvent($event)" 
      type="text" class="form-control" placeholder="Enter Mobile Number">

In Ts file

   keyUpEvent(event: any) {
    const pattern = /[0-9\+\-\ ]/;  
    let inputChar = String.fromCharCode(event.keyCode);

    if (!pattern.test(inputChar)) {
      // invalid character, prevent input
      if(this.Phone.length>0)
      {
        this.Phone= this.Phone.substr(0,this.Phone.length-1);
      }
    }
  }
raj yadav
  • 204
  • 3
  • 10
0

To catch all the event surrounding model changes, can consider using

<input (ngModelChange)="inputFilter($event)"/>

It will detect copy / paste, keyup, any condition that changes the value of the model.

And then:

inputFilter(event: any) {
    const pattern = /[0-9\+\-\ ]/;
    let inputChar = String.fromCharCode(event.charCode);

    if (!pattern.test(inputChar)) {
      // invalid character, prevent input
      event.preventDefault();
    }
}
Brian Ruchiadi
  • 331
  • 1
  • 13
  • 29
0

I use this one:


import { Directive, ElementRef, HostListener, Input, Output, EventEmitter } from '@angular/core';

@Directive({
    selector: '[ngModel][onlyNumber]',
    host: {
        "(input)": 'onInputChange($event)'
    }
})
export class OnlyNumberDirective {

    @Input() onlyNumber: boolean;
    @Output() ngModelChange: EventEmitter<any> = new EventEmitter()

    constructor(public el: ElementRef) {
    }

    public onInputChange($event){
        if ($event.target.value == '-') {
            return;
        }

        if ($event.target.value && $event.target.value.endsWith('.')) {
            return;
        }

        $event.target.value = this.parseNumber($event.target.value);
        $event.target.dispatchEvent(new Event('input'));
    }

    @HostListener('blur', ['$event'])
    public onBlur(event: Event) {
        if (!this.onlyNumber) {
            return;
        }

        this.el.nativeElement.value = this.parseNumber(this.el.nativeElement.value);
        this.el.nativeElement.dispatchEvent(new Event('input'));
    }

    private parseNumber(input: any): any {
        let trimmed = input.replace(/[^0-9\.-]+/g, '');
        let parsedNumber = parseFloat(trimmed);

        return !isNaN(parsedNumber) ? parsedNumber : '';
    }

}

and usage is following

<input onlyNumbers="true" ... />
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63
0

This is the best solution i was able to solve so far (Works even Angular 10):

HTML:

  <input type="text" (input)="inputValidator($event)">

TS file:

  public inputValidator(event: any) {
        const pattern = /^[0-9]*$/;   
        if (!pattern.test(event.target.value)) {//enter only numeric
          event.target.value = event.target.value.replace(/[^0-9]/g, "");
        }
        if(event.target.value == 0){  //avoid 0
          event.target.value = event.target.value.replace(event.target.value,"");
        }
        if(event.target.value > 100000){//to block user enter more than 1000000
          event.target.value = event.target.value.replace(event.target.value,event.target.value.slice(0,6));
        }
      }
0

For floats input

HTML:

<input [(ngModel)]="cost" 
       (keyup)="_keyUp($event)" 
       (keydown)="_keyDown($event)" 
       (blur)="_OnBlur($event)" class="form-control" 
>

TS file:

  //avoid repeating characters when pressing and holding the key
  _keyDown(event: any) {
    if (event.repeat) event.preventDefault();
  }

  _keyUp(event: any) {
    const pattern = /^[-]?([0-9]*[.])?[0-9]+$/;

    if( //allows the writing of the first time of the character
      (event.key == "-" || event.key == ".") &&
      (this.cost.indexOf(event.key) == this.cost.lastIndexOf(event.key))
    ) return true;

    if (!pattern.test(this.cost)) {
      this.cost=this.cost.slice(0,-1);
    }
  }

  //clean the wrong characters when exiting the input
  _OnBlur(event: any) {
    if (this.cost.length>0){
      if (this.cost.slice(-1)=="." || this.cost.slice(-1)=="-") {
        this.cost=this.cost.slice(0,-1);
      }
      if(this.cost.slice(0,1)==".") this.cost="0"+this.cost
    }
  }
  • don't use (keydown), use (input) else you enter in problems when you CTRL+V or BACK, take account when you has text selected or when you are enter text in middle of the input..., you can take a look this [SO](https://stackoverflow.com/questions/58986023/need-angular-directive-digit-number-and-or-two-decimal-in-the-textbox/58991082#58991082) – Eliseo Aug 26 '21 at 17:11
0
In component

const onlyNumberAllow =(e)=> {
const re = /[0-9]+/g;
if (!re.test(e.key)) {
  e.preventDefault();
}}

In HTML--

<input
  type="text"
  maxLength="10"
  name="mobile"
  placeholder="Phone Number"
  value={values.mobile?.replace(undefined, "")}
  invalid={touched.mobile && !!errors.mobile}
  onChange={handleChange}
  onKeyPress={(e) => onlyNumberAllow(e)}
 />
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 21 '22 at 07:52
0

In HTML :

<input(keydown)="restrictCharacters($event)" />

In TS

restrictCharacters(event: KeyboardEvent) {
    const inputKey = event.key;
    const pattern = /^[0-9]+(\.[0-9]+)?$/;
    if (inputChar.length === 1 && !pattern.test(inputChar)) {
        event.preventDefault();
    }
}