97

I am using ngChange in AngularJS to trigger a custom function that will remove any letters the user adds to the input.

<input type="text" name="inputName" data-ng-change="numbersOnly()"/>

The problem is that I need to target the input that triggered numbersOnly() so that I can remove the letters entered. I have looked long and hard on Google and was unable to find anything regarding this.

What can I do?

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
Chris Bier
  • 14,183
  • 17
  • 67
  • 103

17 Answers17

112

Easy way, use type="number" if it works for your use case:

<input type="number" ng-model="myText" name="inputName">

Another easy way: ng-pattern can also be used to define a regex that will limit what is allowed in the field. See also the "cookbook" page about forms.

Hackish? way, $watch the ng-model in your controller:

<input type="text"  ng-model="myText" name="inputName">

Controller:

$scope.$watch('myText', function() {
   // put numbersOnly() logic here, e.g.:
   if ($scope.myText  ... regex to look for ... ) {
      // strip out the non-numbers
   }
})

Best way, use a $parser in a directive. I'm not going to repeat the already good answer provided by @pkozlowski.opensource, so here's the link: https://stackoverflow.com/a/14425022/215945

All of the above solutions involve using ng-model, which make finding this unnecessary.

Using ng-change will cause problems. See AngularJS - reset of $scope.value doesn't change value in template (random behavior)

SPArcheon
  • 1,273
  • 1
  • 19
  • 36
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Ended up creating a directive! Thanks for including the best way. Took a bit of research but I learned a lot! – Chris Bier May 20 '14 at 22:08
  • 1
    Anyone able to expand on downsides to the "easy" way listed first (type="number"), particularly compared to the recommended "best" way ($parser in directive)? – Matt Welch Sep 04 '14 at 15:30
  • 2
    @MattWelch, Late answer but the downside is browser support. Also in Chrome at least, `type=number` will automatically display spinner which may be undesirable. You can hide the spinner via css but then again even that may not work on all browser. – Rosdi Kasim Jan 22 '16 at 13:10
  • 4
    Just two things that might be problematic with the "easy" (type="number") approach ist that 1. type="number" allows negative sign (-), decimal seperator (./,) and exponential notation (e) and 2. on samsung mobile devices you cannot enter negative number in a type="number" field (there is just no minus key on the keyboard) – Aides Nov 25 '16 at 10:41
  • easy way... firefox allows chars to be input into a number only field. It does not update the model, but does display the characters – DRaehal Apr 18 '18 at 21:31
  • `number` type allows "e" to be entered, and on Safari, at least, it displays up/down arrows. – Mike Taverne Sep 14 '20 at 23:07
67

Using ng-pattern on the text field:

<input type="text"  ng-model="myText" name="inputName" ng-pattern="onlyNumbers">

Then include this on your controller

$scope.onlyNumbers = /^\d+$/;
Mistalis
  • 17,793
  • 13
  • 73
  • 97
MarkJ
  • 867
  • 10
  • 13
  • This is what I ended up doing based on Marks answer, thanks for the examples though! I'm sure it will help someone out! – Chris Bier Feb 28 '14 at 15:39
  • 2
    this works almost perfectly, but still allows for 'e' to be entered. – cookies Apr 14 '16 at 17:27
  • Really helps if you are struggling with limiting type="number" and it's length. The solution is to use this ng-pattern and revert back to type="text". Very tidy solution and removes a load of code checking for ng-change or ng-keypress. This solution did not allow 'e's to be entered so I'm assuming that's another issue. – PeterS May 11 '16 at 08:32
  • 1
    Appears to me to be browser specific as to whether it will allow none numeric entry or not. On Chrome simply using is enough and it will not allow any none numeric entry, Firefox on the other hand with same Html will allow any input but triggers the invalid input flag if the value is not numeric. I am looking for a simple way to get Chrome behaviour on all browsers – steve Oct 19 '16 at 07:00
  • Thanks, works perfectly. Only one remark: This will let an initial and end space; to fix this you can use: – Marco Antonio Dec 30 '22 at 13:28
19

Here's my implementation of the $parser solution that @Mark Rajcok recommends as the best method. It's essentially @pkozlowski.opensource's excellent $parser for text answer but rewritten to only allow numerics. All credit goes to him, this is just to save you the 5 minutes of reading that answer and then rewriting your own:

app.directive('numericOnly', function(){
    return {
        require: 'ngModel',
        link: function(scope, element, attrs, modelCtrl) {

            modelCtrl.$parsers.push(function (inputValue) {
                var transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g,'') : null;

                if (transformedInput!=inputValue) {
                    modelCtrl.$setViewValue(transformedInput);
                    modelCtrl.$render();
                }

                return transformedInput;
            });
        }
    };
});

And you'd use it like this:

<input type="text" name="number" ng-model="num_things" numeric-only>

Interestingly, spaces never reach the parser unless surrounded by an alphanumeric, so you'd have to .trim() as needed. Also, this parser does NOT work on <input type="number">. For some reason, non-numerics never make it to the parser where they'd be removed, but they do make it into the input control itself.

Community
  • 1
  • 1
Mordred
  • 3,734
  • 3
  • 36
  • 55
  • When I implemented this, I got JS errors if the input's model was initialised without a value. Making this change resolved that: `var transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g,'') : null;` – Alkie May 26 '15 at 07:25
  • Thanks @Alkie. I added that change into the directive. – Mordred May 26 '15 at 15:15
  • 1
    You have to set `ng-trim` to `false` to make sure spaces reach your parser. – Ilya Mar 31 '16 at 21:13
  • To make it perfect you need to add `modelCtrl.$commitViewValue();` between the $setViewValue(clean); and $render(); – Balazs Nemeth Apr 19 '16 at 13:16
  • 1
    Thank you! This is just awesome! It helped a lot – iulial Nov 01 '16 at 13:19
19

None of the solutions proposed worked fine for me, and after a couple of hours I finally found the way.

This is the angular directive:

angular.module('app').directive('restrictTo', function() {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var re = RegExp(attrs.restrictTo);
            var exclude = /Backspace|Enter|Tab|Delete|Del|ArrowUp|Up|ArrowDown|Down|ArrowLeft|Left|ArrowRight|Right/;

            element[0].addEventListener('keydown', function(event) {
                if (!exclude.test(event.key) && !re.test(event.key)) {
                    event.preventDefault();
                }
            });
        }
    }
});

And the input would look like:

<input type="number" min="0" name="inputName" ng-model="myModel" restrict-to="[0-9]">

The regular expression evaluates the pressed key, not the value.

It also works perfectly with inputs type="number" because prevents from changing its value, so the key is never displayed and it does not mess with the model.

ragnar
  • 1,252
  • 11
  • 18
7

Here is a pretty good solution to makes only allow enter number to the input:

<input type="text" ng-model="myText" name="inputName" onkeypress='return event.charCode >= 48 && event.charCode <= 57'/>
Mistalis
  • 17,793
  • 13
  • 73
  • 97
Raymond Liao
  • 1,799
  • 3
  • 20
  • 32
4

There are a few ways to do this.

You could use type="number":

<input type="number" />

Alternatively - I created a reuseable directive for this that uses a regular expression.

Html

<div ng-app="myawesomeapp">
    test: <input restrict-input="^[0-9-]*$" maxlength="20" type="text" class="test" />
</div>

Javascript

;(function(){
    var app = angular.module('myawesomeapp',[])
    .directive('restrictInput', [function(){

        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                var ele = element[0];
                var regex = RegExp(attrs.restrictInput);
                var value = ele.value;

                ele.addEventListener('keyup',function(e){
                    if (regex.test(ele.value)){
                        value = ele.value;
                    }else{
                        ele.value = value;
                    }
                });
            }
        };
    }]);    
}());
Peter Rasmussen
  • 16,474
  • 7
  • 46
  • 63
  • use, $(element).on('input', function() { // your logic }); this will prevent even entering the unwanted value – Vishal Mar 16 '17 at 12:48
3

All the above solutions are quite large, i wanted to give my 2 cents on this.

I am only checking if the value inputed is a number or not, and checking if it's not blank, that's all.

Here is the html:

<input type="text" ng-keypress="CheckNumber()"/>

Here is the JS:

$scope.CheckKey = function () {
    if (isNaN(event.key) || event.key === ' ' || event.key === '') {
        event.returnValue = '';
    }
};

It's quite simple.

I belive this wont work on Paste tho, just so it's known.

For Paste, i think you would need to use the onChange event and parse the whole string, quite another beast the tamme. This is specific for typing.

UPDATE for Paste: just add this JS function:

$scope.CheckPaste = function () {
    var paste = event.clipboardData.getData('text');

    if (isNaN(paste)) {
        event.preventDefault();
        return false;
    }
};

And the html input add the trigger:

<input type="text" ng-paste="CheckPaste()"/>

I hope this helps o/

Zorkind
  • 607
  • 12
  • 21
2

Here is a Plunker handling any situation above proposition do not handle.
By using $formatters and $parsers pipeline and avoiding type="number"

And here is the explanation of problems/solutions (also available in the Plunker) :

/*
 *
 * Limit input text for floating numbers.
 * It does not display characters and can limit the Float value to X numbers of integers and X numbers of decimals.
 * min and max attributes can be added. They can be Integers as well as Floating values.
 *
 * value needed    |    directive
 * ------------------------------------
 * 55              |    max-integer="2"
 * 55.55           |    max-integer="4" decimal="2" (decimals are substracted from total length. Same logic as database NUMBER type)
 *
 *
 * Input type="number" (HTML5)
 *
 * Browser compatibility for input type="number" :
 * Chrome : - if first letter is a String : allows everything
 *          - if first letter is a Integer : allows [0-9] and "." and "e" (exponential)
 * Firefox : allows everything
 * Internet Explorer : allows everything
 *
 * Why you should not use input type="number" :
 * When using input type="number" the $parser pipeline of ngModel controller won't be able to access NaN values.
 * For example : viewValue = '1e'  -> $parsers parameter value = "".
 * This is because undefined values are not allowes by default (which can be changed, but better not do it)
 * This makes it impossible to modify the view and model value; to get the view value, pop last character, apply to the view and return to the model.
 *
 * About the ngModel controller pipelines :
 * view value -> $parsers -> model value
 * model value -> $formatters -> view value
 *
 * About the $parsers pipeline :
 * It is an array of functions executed in ascending order.
 * When used with input type="number" :
 * This array has 2 default functions, one of them transforms the datatype of the value from String to Number.
 * To be able to change the value easier (substring), it is better to have access to a String rather than a Number.
 * To access a String, the custom function added to the $parsers pipeline should be unshifted rather than pushed.
 * Unshift gives the closest access to the view.
 *
 * About the $formatters pipeline :
 * It is executed in descending order
 * When used with input type="number"
 * Default function transforms the value datatype from Number to String.
 * To access a String, push to this pipeline. (push brings the function closest to the view value)
 *
 * The flow :
 * When changing ngModel where the directive stands : (In this case only the view has to be changed. $parsers returns the changed model)
 *     -When the value do not has to be modified :
 *     $parsers -> $render();
 *     -When the value has to be modified :
 *     $parsers(view value) --(does view needs to be changed?) -> $render();
 *       |                                  |
 *       |                     $setViewValue(changedViewValue)
 *       |                                  |
 *       --<-------<---------<--------<------
 *
 * When changing ngModel where the directive does not stand :
 *     - When the value does not has to be modified :
 *       -$formatters(model value)-->-- view value
 *     -When the value has to be changed
 *       -$formatters(model vale)-->--(does the value has to be modified) -- (when loop $parsers loop is finished, return modified value)-->view value
 *                                              |
 *                                  $setViewValue(notChangedValue) giving back the non changed value allows the $parsers handle the 'bad' value
 *                                               |                  and avoids it to think the value did not changed
 *                Changed the model <----(the above $parsers loop occurs)
 *
 */
gr3g
  • 2,866
  • 5
  • 28
  • 52
1
   <input type="text" name="profileChildCount" id="profileChildCount" ng-model="profile.ChildCount" numeric-only maxlength="1" />

you can use numeric-only attribute .

tahsin ilhan
  • 208
  • 2
  • 12
1

DECIMAL

directive('decimal', function() {
                return {
                    require: 'ngModel',
                    restrict: 'A',
                    link: function(scope, element, attr, ctrl) {
                        function inputValue(val) {
                            if (val) {
                                var digits = val.replace(/[^0-9.]/g, '');

                                if (digits.split('.').length > 2) {
                                    digits = digits.substring(0, digits.length - 1);
                                }

                                if (digits !== val) {
                                    ctrl.$setViewValue(digits);
                                    ctrl.$render();
                                }
                                return parseFloat(digits);
                            }
                            return "";
                        }
                        ctrl.$parsers.push(inputValue);
                    }
                };
            });

DIGITS

directive('entero', function() {
            return {
                require: 'ngModel',
                restrict: 'A',
                link: function(scope, element, attr, ctrl) {
                    function inputValue(val) {
                        if (val) {
                            var value = val + ''; //convert to string
                            var digits = value.replace(/[^0-9]/g, '');

                            if (digits !== value) {
                                ctrl.$setViewValue(digits);
                                ctrl.$render();
                            }
                            return parseInt(digits);
                        }
                        return "";
                    }
                    ctrl.$parsers.push(inputValue);
                }
            };
        });

angular directives for validate numbers

Angeldev
  • 164
  • 1
  • 4
0

I know this is old, but I've created a directive for this purpose in case anyone is looking for an easy solution. Very simple to use.

You can check it out here.

cohenadair
  • 2,072
  • 1
  • 22
  • 38
0

you may also want to remove the 0 at the beginning of the input... I simply add an if block to Mordred answer above because I cannot make a comment yet...

  app.directive('numericOnly', function() {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, modelCtrl) {

          modelCtrl.$parsers.push(function (inputValue) {
              var transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g,'') : null;

              if (transformedInput!=inputValue) {
                  modelCtrl.$setViewValue(transformedInput);
                  modelCtrl.$render();
              }
              //clear beginning 0
              if(transformedInput == 0){
                modelCtrl.$setViewValue(null);
                modelCtrl.$render();
              }
              return transformedInput;
          });
      }
    };
  })
sireken
  • 29
  • 4
0

Basic HTML

<input type="number" />

Basic bootstrap

<input class="form-control" type="number" value="42" id="my-id">
Amr Ibrahim
  • 2,066
  • 2
  • 23
  • 47
0

Try this,

<input ng-keypress="validation($event)">

 function validation(event) {
    var theEvent = event || window.event;
    var key = theEvent.keyCode || theEvent.which;
    key = String.fromCharCode(key);
    var regex = /[0-9]|\./;
    if (!regex.test(key)) {
        theEvent.returnValue = false;
        if (theEvent.preventDefault) theEvent.preventDefault();
    }

}
Joee
  • 1,834
  • 18
  • 19
0

SOLUTION: I make a directive for all inputs, number, text, or any, in the app, so you can input a value and change the event. Make for angular 6

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

 @Directive({
// tslint:disable-next-line:directive-selector
selector: 'input[inputType]'
})
  export class InputTypeDirective {
 constructor(private _el: ElementRef) {}

 @Input() inputType: string;
 // tipos: number, letter, cuit, tel

@HostListener('input', ['$event']) onInputChange(event) {
if (!event.data) {
  return;
}

switch (this.inputType) {
  case 'number': {
    const initalValue = this._el.nativeElement.value;
    this._el.nativeElement.value = initalValue.replace(/[^0-9]*/g, '');
    if (initalValue !== this._el.nativeElement.value) {
      event.stopPropagation();
    }
     break;
          }
       case 'text': {
        const result = event.data.match(/[^a-zA-Z Ññ]*/g);
        if (result[0] !== '') {
           const initalValue = this._el.nativeElement.value;
           this._el.nativeElement.value = initalValue.replace(
          /[^a-zA-Z Ññ]*/g,
           ''
         );
           event.stopPropagation();
        }
        break;
    }
        case 'tel':
          case 'cuit': {
         const initalValue = this._el.nativeElement.value;
      this._el.nativeElement.value = initalValue.replace(/[^0-9-]*/g, '');
       if (initalValue !== this._el.nativeElement.value) {
         event.stopPropagation();
       }
     }
   }
  }
   }

HTML

     <input matInput inputType="number" [formControlName]="field.name" [maxlength]="field.length" [placeholder]="field.label | translate"  type="text" class="filter-input">
Seba Arce
  • 31
  • 3
-1

I ended up creating a modified directive of the above code to accept input and change the format on the fly...

.directive('numericOnly', function($filter) {
  return {
      require: 'ngModel',
      link: function(scope, element, attrs, modelCtrl) {

           element.bind('keyup', function (inputValue, e) {
             var strinput = modelCtrl.$$rawModelValue;
             //filter user input
             var transformedInput = strinput ? strinput.replace(/[^,\d.-]/g,'') : null;
             //remove trailing 0
             if(transformedInput.charAt(0) <= '0'){
               transformedInput = null;
               modelCtrl.$setViewValue(transformedInput);
               modelCtrl.$render();
             }else{
               var decimalSplit = transformedInput.split(".")
               var intPart = decimalSplit[0];
               var decPart = decimalSplit[1];
               //remove previously formated number
               intPart = intPart.replace(/,/g, "");
               //split whole number into array of 3 digits
               if(intPart.length > 3){
                 var intDiv = Math.floor(intPart.length / 3);
                 var strfraction = [];
                 var i = intDiv,
                     j = 3;

                 while(intDiv > 0){
                   strfraction[intDiv] = intPart.slice(intPart.length-j,intPart.length - (j - 3));
                   j=j+3;
                   intDiv--;
                 }
                 var k = j-3;
                 if((intPart.length-k) > 0){
                   strfraction[0] = intPart.slice(0,intPart.length-k);
                 }
               }
               //join arrays
               if(strfraction == undefined){ return;}
                 var currencyformat = strfraction.join(',');
                 //check for leading comma
                 if(currencyformat.charAt(0)==','){
                   currencyformat = currencyformat.slice(1);
                 }

                 if(decPart ==  undefined){
                   modelCtrl.$setViewValue(currencyformat);
                   modelCtrl.$render();
                   return;
                 }else{
                   currencyformat = currencyformat + "." + decPart.slice(0,2);
                   modelCtrl.$setViewValue(currencyformat);
                   modelCtrl.$render();
                 }
             }
            });
      }
  };

})

sireken
  • 29
  • 4
-1
<input type="text" ng-model="employee.age" valid-input input-pattern="[^0-9]+" placeholder="Enter an age" />

<script>
var app = angular.module('app', []);

app.controller('dataCtrl', function($scope) {
});

app.directive('validInput', function() {
  return {
    require: '?ngModel',
    scope: {
      "inputPattern": '@'
    },
    link: function(scope, element, attrs, ngModelCtrl) {

      var regexp = null;

      if (scope.inputPattern !== undefined) {
        regexp = new RegExp(scope.inputPattern, "g");
      }

      if(!ngModelCtrl) {
        return;
      }

      ngModelCtrl.$parsers.push(function(val) {
        if (regexp) {
          var clean = val.replace(regexp, '');
          if (val !== clean) {
            ngModelCtrl.$setViewValue(clean);
            ngModelCtrl.$render();
          }
          return clean;
        }
        else {
          return val;
        }

      });

      element.bind('keypress', function(event) {
        if(event.keyCode === 32) {
          event.preventDefault();
        }
      });
    }
}}); </script>
rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • 1
    Code dumping is usually frowned upon. Please add some explanation. – rayryeng Jun 17 '16 at 08:45
  • 1
    for restrict key press try this - - - function Number(evt){ var charCode = (evt.which) ? evt.which : event.keyCode if (charCode > 31 && (charCode < 48 || charCode > 57)) return false; return true; – Rahul Sharma Jun 17 '16 at 10:09