AngularJS 中限制正则表达式输入的指令

Directive for restricting typing by Regex in AngularJS

我编写了一个 angular 指令,用于通过指定正则表达式来禁止从输入中键入内容。在该指令中,我指出了一个将用于允许输入数据的正则表达式。从概念上讲,它工作正常,但此解决方案中有两个错误:

  1. 在第一个 Plunker 示例中,输入必须 仅允许数字 数字后跟一个点 [.],或 数字后跟一个点,然后是不超过四位数的数字
    • 如果我输入一个值 '1.1111' 然后我转到第一个数字然后输入另一个数字(为了得到一个值 '11.1111') ,没有任何反应。错误在于我在我的正则表达式验证器上使用了表达式 elem.val() + event.key。我不知道如何获得整个 按键事件输入的当前值;
  2. 第二个是一些字符(重音符、锐音符、波浪号、抑扬符)允许在输入时使用(不止一次按下其中一个),尽管正则表达式不允许它们。

我需要在代码中进行哪些更改才能通过正则表达式获得有效的类型限制?

  <html ng-app="app">    
  <head>
    <script data-require="angularjs@1.6.4" data-semver="1.6.4" src="https://code.angularjs.org/1.6.4/angular.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Restrict typing by RegExp</h1>
    PATTERN 1 (^\d+$|^\d+[.]$|^\d+[.]\d{1,4}$) <input type="text" allow-typing="^\d+$|^\d+[.]$|^\d+[.]\d{1,4}$"/><br>
    ONLY NUMBERS <input type="text" allow-typing="^[0-9]+$"/><br>
    ONLY STRINGS <input type="text" allow-typing="^[a-zA-Z]+$"/>
  </body>

</html>

指令

angular.module('app', []).directive('allowTyping', function() {
  return {
    restrict: 'A',
    link: function(scope, elem, attrs, ctrl) {
      var regex = attrs.allowTyping;
      elem.bind('keypress', function(event) {
        var input = elem.val() + event.key;
        var validator = new RegExp(regex);
        if(!validator.test(input)) {
          event.preventDefault();
          return false;
        }
      });
    }
  };
});
  1. 您在正则表达式中指定了 4 种不同的模式 3 种不同的模式,由更改符号分隔:^\d+$|^\d+[.]$|^\d+[.]\d{1,4}$ - 这将不满足 input must allow only numbers followed by a dot [.], followed by a number with no more than four digits. 的条件 错误 "where nothing happens" 的发生是因为您要检查的变量不是您认为的那样,请检查屏幕截图以了解如何检查它,以及它是什么:

  1. 无法重现。

如果这是我的代码,我会完全改变策略:我会监听 input 事件,而不是尝试对用户与该字段的交互进行微观管理。

总的来说,您采用的方法存在问题。最大的一个是 keypress 不会针对字段的所有更改发出。 值得注意的是,

  1. 它不是由 DELETE 和 BACKSPACE 键触发的。

  2. 输入法可以绕过。当您将变音符号作为变音符号输入时,您的代码并未记录更改。通常,如果用户使用 input method,则无法保证添加到该字段的每个新字符都会导致 keypress 事件。这取决于用户选择的方法。

  3. 当用户从字段中剪切或粘贴到字段中时,
  4. keypress 没有帮助。

您可以添加代码来尝试处理上述所有情况,但这会很快变得复杂。您已经 运行 遇到了 elem.val() + event.key 的问题,因为 keypress 可能 并不总是 是关于在字段末尾插入的字符。用户可能已经移动了插入符号,因此您必须跟踪插入符号的位置。一条评论建议收听 keyup 但这对输入法或 paste/cut 事件没有帮助。

相比之下,input event是在字段的值发生变化时生成的,因为变化发生了。以上所有情况均已处理。例如,这会起作用:

elem.bind('input', function(event) {
  var validator = new RegExp(regex);
  elem.css("background-color", !validator.test(elem.val()) ? "red" : null);
});

这是一个最小的说明,您可以将其放入 fiddle 以替换当前的事件处理程序。在实际的应用程序中,我会给用户一个详细的错误消息,而不是仅仅更改字段的颜色,我会在事件处理程序之外只创建一次 validator,但这给了你想法。

(还有一个 change 事件,但您不想使用它。对于文本字段,它是在焦点离开字段时生成的,但为时已晚。)

根据您的方法参见Plnkr Fixed

原因和变化的解释如下。 旁注:我不会以这种方式实现它(将 ngModel$parsers$formatters 一起使用,例如 ) - implementing that is beyond the scope of your question. However I found a full implementation by regexValidate by Ben Lesh 这将适合您的问题域:-

If I type a value '1.1111' and after that I go to the first digit and so type another digit (in order to get a value as '11.1111') , nothing happening.

因为在你下面的代码中

var input = elem.val() + event.key;

您假设 event.key 始终附加在末尾。

那么如何获取正确位置的位置并验证重构的字符串呢?您可以使用未记录的 event.target.selectionStart 属性。请注意,即使您没有选择任何内容,您也会填充此内容(IE 11 和其他浏览器)。参见 Plnkr Fixed

The second one is the fact that some characters (grave, acute, tilde, circumflex) are being allowed on typing (press one of them more than once), althought the regex does not allow them.

修复了正则表达式 - 更正以下一个:

^[0-9]*(?:\.[0-9]{0,4})?$

所以整个事情看起来如下

  link: function(scope, elem, attrs, ctrl) {
    var regex = attrs.allowTyping;
    elem.bind('keypress', function(event) {
      var pos = event.target.selectionStart;
      var oldViewValue = elem.val();
      var input = newViewValue(oldViewValue, pos, event.key);
      console.log(input);
      var validator = new RegExp(regex);
      if (!validator.test(input)) {
        event.preventDefault();
        return false;
      }
    });

    function newViewValue(oldViewValue, pos, key) {
      if (!oldViewValue) return key;
      return [oldViewValue.slice(0, pos), key, oldViewValue.slice(pos)].join('');
    }
  }

您可以将事件更改为 keyup,这样测试将 运行 添加每个额外的字符之后。

这意味着您需要保存最后一个有效输入,因此如果用户尝试插入一个会使字符串无效的字符,测试将恢复最后一个有效值。

因此,更新后的指令:

angular.module('app', [])
    .directive('allowTyping', function()  {
        return {
            restrict : 'A',
            link : function(scope, elem, attrs, ctrl) {
                var regex = attrs.allowTyping;
                var lastInputValue = "";
                elem.bind('keyup', function(event) {
                var input = elem.val();
                var validator = new RegExp(regex);

                if (!validator.test(input))
                    // Restore last valid input
                    elem.val(lastInputValue).trigger('input');
                else
                    // Update last valid input
                    lastInputValue = input;
                });
            }
        };
    });