Properly filtering an input field in Angular

耗尽温柔 提交于 2019-12-11 13:56:15

问题


I found this directive for filtering a field for currency, so a user just needs to type and the decimal is implied.

It works well, except that a user is able to put in a letter if they press a letter twice. I don't want them to be able to put in a letter at all.

I tried changing the type="number" but then it would only allow 5 digits before disappearing inexplicably. You can see this at this plunker:

http://plnkr.co/edit/Y1olj7Cdz7XKqRmE7BFb?p=preview

So either I need to change something (the regex?) in this line:

var plainNumber = viewValue.replace(/[^\d|\-+]/g, '');

or somehow fix the type="number" to keep input past five digits. Anybody know what to do here?


回答1:


What I got from your issue are :

  1. only accept numbers, replace everything else with ''
  2. keep 2 decimal digits
  3. filter user's input and reflect/modify the value to the input element as user types

When binding the input value with ng-model, actually there are two main values behind the scenes internally: $viewValue and $modelValue. And a simple relationship between them is like this:

input DOM value -> $viewValue -> $modelValue

The $parsers only work between $viewValue and $modelValue. So the problem is when you insert a new parser into the $parsers, the modified value will only affect the $modleValue as you simply return a new value in the parse function.

$parsers are :

Array of functions to execute, as a pipeline, whenever the control reads value from the DOM. The functions are called in array order, each passing its return value through to the next. The last return value is forwarded to the $validators collection

more on : https://docs.angularjs.org/api/ng/type/ngModel.NgModelController#$parsers

And the returned value in the parser function WON'T affect the $viewValue so what you need to do is to also change that value and reflect on the input DOM element.

Code example:

app.directive('format', ['$filter', function ($filter) {
    return {
        require: 'ngModel',

        link: function (scope, elem, attrs, ctrl) {
            if (!ctrl) return;

            ctrl.$parsers.unshift(function (viewValue) {
                var plainNumber = viewValue.replace(/[^\d|\-+]/g, '');
                //use angular internal 'number' filter
                plainNumber = $filter('number')(plainNumber/100,2);

                //update the $viewValue
                ctrl.$setViewValue(plainNumber);
                //reflect on the DOM element
                ctrl.$render();
                //return the modified value to next parser
                return plainNumber;
            });
        }
    };
}]);

A working plnkr is here: http://plnkr.co/edit/aoBWhJhlJLiEhBm5hRNX?p=preview

At last, the reason why when you type 6th digit and it disappear is:

As you type 6 digits, the browser will convert and format the number you type to this kind of format: '1,234.56' (pay attention to the COMMA ',', the separator may defer on different browsers) , and you will see a warning given by angular in the console :

The specified value "1,234.56" is not a valid number. The value must match to the following regular expression: -?(\d+|\d+.\d+|.\d+)([eE][-+]?\d+)?

which is caused by the restriction from the type='number' on the input element, angular will perform extra internal validation to match the rule defined by angular itself.




回答2:


I'm unfamiliar with AngularJS, but I can tell your regex is not exactly what you intended.

You have this: [^\d|\-+]

This is a negated char class. All non-digits, non +s, non -s, AND non |s will match. Also note that \d contains more than [0-9], because it will likely match Persian digits and such.

I would use: [^-+0-9]. By putting the - directly after the ^ it does not need to be escaped.

The problem with your solution is you're waiting for the characters to be typed before you remove them. The keystrokes may happen faster than your field can purge them.


Despite having no experience whatsoever in Angular or jQuery,

I made something that works perfectly, based off of here:

It allows 0-9 to be typed freely. When a + or - is typed, it is never added to the end. Instead, it appears at the beginning, overwriting any existing sign. Maybe you should just run the snippet and see yourself:

var app = angular.module('App', []);

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

app.directive('inputRestrictor', [function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attr, ngModelCtrl) {
      var pattern = /[^+0-9-]/g;

      function fromUser(text) {
        if (!text)
          return text;

        var rep = /[+]/g;
        var rem = /[-]/g;
        rep.exec(text);
        rem.exec(text);
        var indexp = rep.lastIndex;
        var indexm = rem.lastIndex;
        text = text.replace(/[+-]/g, '');
        if (indexp > 0 || indexm > 0) {
          if (indexp > indexm) text = "+" + text;
          else text = "-" + text;
        }
        var transformedInput = text.replace(pattern, '');
        ngModelCtrl.$setViewValue(transformedInput);
        ngModelCtrl.$render();
        return transformedInput;
      }
      ngModelCtrl.$parsers.push(fromUser);
    }
  };
}]);
<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@1.3.0-rc.2" data-semver="1.3.0-rc.2" src="  https://code.angularjs.org/1.3.0-rc.2/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-app="App">
    <div ng-controller="MainCtrl">
      <input type="text"  ng-model="inputValue" input-restrictor>
      <input type="submit" ng-click="getdata()" />
    </div>
  </body>

</html>


来源:https://stackoverflow.com/questions/36650153/properly-filtering-an-input-field-in-angular

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!