Директива ограничения ввода Angular — отрицание регулярных выражений

РЕДАКТИРОВАТЬ: Пожалуйста, не стесняйтесь добавлять дополнительные проверки, которые были бы полезны для других, используя эту простую директиву.

--

Я пытаюсь создать директиву Angular, которая ограничивает ввод символов в текстовое поле. Я добился успеха с парой распространенных вариантов использования (буквенный, буквенно-цифровой и числовой), но используя популярные методы проверки адресов электронной почты, дат и валюты, я не могу заставить директиву работать, так как мне нужно, чтобы она отрицала регулярное выражение. По крайней мере, это то, что я думаю, что это нужно сделать.

Приветствуется любая помощь в выборе валюты (необязательный разделитель тысяч и центов), даты (мм/дд/гггг) и электронной почты. Я вообще не силен в регулярных выражениях.

Вот что у меня есть на данный момент: http://jsfiddle.net/corydorning/bs05ys69/

HTML

<div ng-app="example">
<h1>Validate Directive</h1>

<p>The Validate directive allow us to restrict the characters an input can accept.</p>

<h3><code>alphabetical</code> <span style="color: green">(works)</span></h3>
<p>Restricts input to alphabetical (A-Z, a-z) characters only.</p>
<label><input type="text" validate="alphabetical" ng-model="validate.alphabetical"/></label>

<h3><code>alphanumeric</code> <span style="color: green">(works)</span></h3>
<p>Restricts input to alphanumeric (A-Z, a-z, 0-9) characters only.</p>
<label><input type="text" validate="alphanumeric" ng-model="validate.alphanumeric" /></label>

<h3><code>currency</code> <span style="color: red">(doesn't work)</span></h3>
<p>Restricts input to US currency characters with comma for thousand separator (optional) and cents (optional).</p>
<label><input type="text" validate="currency.us" ng-model="validate.currency" /></label>

<h3><code>date</code> <span style="color: red">(doesn't work)</span></h3>
<p>Restricts input to the mm/dd/yyyy date format only.</p>
<label><input type="text" validate="date" ng-model="validate.date" /></label>

<h3><code>email</code> <span style="color: red">(doesn't work)</span></h3>
<p>Restricts input to email format only.</p>
<label><input type="text" validate="email" ng-model="validate.email" /></label>

<h3><code>numeric</code> <span style="color: green">(works)</span></h3>
<p>Restricts input to numeric (0-9) characters only.</p>
<label><input type="text" validate="numeric" ng-model="validate.numeric" /></label>

JavaScript

angular.module('example', [])
  .directive('validate', function () {
    var validations = {
      // works
      alphabetical: /[^a-zA-Z]*$/,

      // works
      alphanumeric: /[^a-zA-Z0-9]*$/,

      // doesn't work - need to negate?
      // taken from: http://stackoverflow.com/questions/354044/what-is-the-best-u-s-currency-regex
      currency: /^[+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]{2})?$/,

      // doesn't work - need to negate?
      // taken from here: http://stackoverflow.com/questions/15196451/regular-expression-to-validate-datetime-format-mm-dd-yyyy
      date: /(?:0[1-9]|1[0-2])\/(?:0[1-9]|[12][0-9]|3[01])\/(?:19|20)[0-9]{2}/,

      // doesn't work - need to negate?
      // taken from: http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
      email: /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i,

      // works
      numeric: /[^0-9]*$/
    };

  return {
    require: 'ngModel',

    scope: {
      validate: '@'
    },

    link: function (scope, element, attrs, modelCtrl) {
      var pattern = validations[scope.validate] || scope.validate
      ;

      modelCtrl.$parsers.push(function (inputValue) {
        var transformedInput = inputValue.replace(pattern, '')
        ;

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

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

person CoryDorning    schedule 27.10.2015    source источник
comment
Предполагается ли, что вы хотите исправить ввод только с недопустимыми символами в конце ввода (ср. использование $ в первых регулярных выражениях)? Я бы это понял, потому что при вводе в середине строки неудобно, что корректор передвигает каретку в конец. С другой стороны, это не помешает пользователям вставлять недопустимые символы... Просто скажите мне, ожидаете ли вы, что проверка будет отклонять только строки с неправильными окончаниями.   -  person trincot    schedule 29.10.2015
comment
Я бы предпочел, чтобы он соответствовал формату, когда пользователь вводит его. если он не соответствует формату, то ключ нельзя ввести.   -  person CoryDorning    schedule 02.11.2015
comment
Тогда как вы ожидаете, что кто-то введет дату? Если вы начнете печатать с нуля, то при вводе первой цифры ваш ввод будет отклонен, так как это недействительная дата...   -  person trincot    schedule 02.11.2015
comment
Отмеченный ответ работает очень хорошо...   -  person CoryDorning    schedule 02.11.2015


Ответы (3)


Я почти уверен, что есть лучший способ, возможно, регулярное выражение также не лучший инструмент для этого, но вот мое предложение.

Таким образом, вы можете только ограничить, какие символы разрешены для ввода, и заставить пользователя использовать правильный формат, но вам также нужно будет проверить окончательный ввод после того, как пользователь закончит печатать, но это уже другая история.

Алфавитные, числовые и буквенно-цифровые символы довольно просты для ввода и проверки ввода, поскольку ясно, что вы можете ввести, и что является правильным окончательным вводом. Но с датами, почтой, валютой вы не можете проверить ввод с помощью регулярного выражения для полного действительного ввода, так как пользователю нужно сначала ввести его, а тем временем ввод должен быть недействительным с точки зрения окончательного действительного ввода. Итак, одно дело, например, запретить пользователю вводить только цифры и / для формата даты, например: 12/12/1988, но, в конце концов, вам нужно проверить, правильно ли он ввел дату или, например, только 12/12/126. Это необходимо проверять, когда ответ отправляется пользователем, или когда текстовое поле теряет фокус и т. д.

Чтобы просто проверить введенный символ, вы можете попробовать следующее:

JSFiddle DEMO

Первое изменение:

var transformedInput = inputValue.replace(pattern, '')

to

var transformedInput = inputValue.replace(pattern, '$1')

затем используйте регулярные выражения:

  • /^([a-zA-Z]*(?=[^a-zA-Z]))./ - алфавитный
  • /^([a-zA-Z0-9]*(?=[^a-zA-Z0-9]))./ - буквенно-цифровой
  • /(\.((?=[^\d])|\d{2}(?![^,\d.]))|,((?=[^\d])|\d{3}(?=[^,.$])|(?=\d{1,2}[^\d]))|\$(?=.)|\d{4,}(?=,)).|[^\d,.$]|^\$/- валюта (разрешить строку типа: 343243,34, 1 123 345,34, 0,05 с долларом или без него)
  • ^(((0[1-9]|1[012])|(\d{2}\/\d{2}))(?=[^\/])|((\d)|(\d{2}\/\d{2}\/\d{1,3})|(.+\/))(?=[^\d])|\d{2}\/\d{2}\/\d{4}(?=.)).|^(1[3-9]|[2-9]\d)|((?!^)(3[2-9]|[4-9]\d)\/)|[3-9]\d{3}|2[1-9]\d{2}|(?!^)\/\d\/|^\/|[^\d/] - дата (00-12/00-31/0000-2099)
  • /^(\d*(?=[^\d]))./ - числовой
  • /^([\w.$-]+\@[\w.]+(?=[^\w.])|[\w.$-]+\@(?=[^\w.-])|[\w.@-]+(?=[^\w.$@-])).$|\.(?=[^\w-@]).|[^\w.$@-]|^[^\w]|\.(?=@).|@(?=\.)./i - электронная почта

Как правило, он использует этот шаблон:

([valid characters or structure] captured in group $1)(?= positive lookahead for not allowed characters) any character

по сути, он захватит все допустимые символы в группе $1, и если пользователь введет недопустимый символ, вся строка будет заменена уже захваченными допустимыми символами из группы $1. Он дополняется частью, которая должна исключать некоторые очевидные недопустимые символы, такие как @@ в почте или 34...2 в деньгах.

Поняв, как работают эти регулярные выражения, несмотря на то, что они выглядят довольно сложными, я думаю, что их легко расширить, добавив дополнительные разрешенные/недопустимые символы.

Регулярное выражение для проверки валюты, дат и почты найти несложно, поэтому я считаю излишним публиковать их здесь.

Не по теме. Более того, часть currency в вашем демо не работает, потому что: validate="currency.us" вместо validate="currency", или, по крайней мере, она работает после этой модификации.

person m.cekiera    schedule 30.10.2015
comment
В примере с валютой $ разрешено размещать в конце поля. Как я могу удалить его, чтобы он не был разрешен или просто разрешен в начале? - person CoryDorning; 02.11.2015
comment
Использование этого регулярного выражения для валюты запрещает использование символа $: /(\.((?=[^\d])|\d{2}(?![^,\d.]))|,((?=[^\d])|\d{3}(?=[^,.])|(?=\d{1,2}[^\d]))|\$(?=.)|\d{4,}(?=,)).|[^\d,.]|^\$/ - person CoryDorning; 02.11.2015
comment
@CoryDorning извините, я думал, что $ должно быть в конце. Вы можете использовать /(\.((?=[^\d])|\d{2}(?![^,\d.]))|,((?=[^\d])|\d{3}(?=[^,.$])|(?=\d{1,2}[^\d]))|\d{4,}(?=,)|.(?=\$)).|[^\d,.$]/ для чисел с $ в начале или без него или добавить ^[^$] для разрешения только строк с $ в начале - person m.cekiera; 02.11.2015

На мой взгляд, невозможно создать регулярные выражения, которые будут работать для сопоставления таких вещей, как даты или электронные письма, с используемым вами парсером. В основном это связано с тем, что вам потребуются группы без захвата в ваших регулярных выражениях (что возможно), которые не заменяются вызовом inputValue.replace(pattern, ''), который у вас есть в вашей функции парсера. И это та часть, которая невозможна в JavaScript. JavaScript также заменяет то, что вы помещаете в группы без захвата.

Итак... вам нужно пойти на другой подход. Я бы предложил перейти к положительным регулярным выражениям, которые дадут совпадение, когда ввод действителен. Затем вам нужно, конечно, изменить код вашего парсера. Например, вы можете решить обрезать символы с конца вводимого текста до тех пор, пока то, что останется, не пройдет тест регулярного выражения. Это вы можете закодировать следующим образом:

  modelCtrl.$parsers.push(function (inputValue) {
    var transformedInput = inputValue;
    while (transformedInput && !pattern.exec(transformedInput)) {
       // validation fails: chop off last character and try again
       transformedInput = transformedInput.slice(0, -1);
    }

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

    return transformedInput;
  });

Теперь жить стало немного легче. Просто обратите внимание, что вы делаете ваши регулярные выражения таким образом, чтобы они не отвергали частичный ввод. Таким образом, "01/" следует считать действительным для даты, иначе пользователь никогда не сможет ввести дату. С другой стороны, как только становится ясно, что добавление символов больше не позволяет вводить корректный ввод, регулярное выражение должно отклонить его. Таким образом, «101» следует отклонить как дату, поскольку вы никогда не сможете добавлять символы в конце, чтобы сделать ее действительной датой.

Кроме того, все эти регулярные выражения должны проверять весь ввод, поэтому в них необходимо использовать символы ^ и $.

Вот как может выглядеть регулярное выражение для (частичной) даты:

^([0-9]{0,2}|[0-9]{2}[\/]([0-9]{0,2}|[0-9]{2}[\/][0-9]{0,4}))$

Это означает: допустим ввод от 0 до 2 цифр или ровно 2 цифры, за которыми следует косая черта, за которой следует либо:

  1. от 0 до 2 цифр или
  2. ровно 2 цифры, за которыми следует косая черта, а затем от 0 до 4 цифр

По общему признанию, не такой умный, как тот, который вы нашли, но он потребует много редактирования, чтобы разрешить частично введенные даты. Это возможно, но это очень длинное выражение с большим количеством скобок и |.

Как только вы настроите все регулярные выражения, вы можете подумать о дальнейшем улучшении синтаксического анализатора. Одна из идей состоит в том, чтобы не позволять ему обрезать символы с конца, а позволить ему проверять все строки с одним удаленным символом по сравнению с оригиналом и смотреть, какая из них проходит тест. Если не найден способ удалить один символ и добиться успеха, удалите два последовательных символа в любом месте входного значения, затем три и т. д., пока не найдете значение, которое проходит тест, или не получите пустое значение. .

Это будет лучше работать в случаях, когда пользователь вставляет символы наполовину. Просто идея...

person trincot    schedule 29.10.2015

person    schedule
comment
Пояснительный текст о том, как ваш код отвечает на вопрос, сделает ваш ответ более полезным для других пользователей. - person Nick; 17.04.2019