Подтвердить что ты не робот

Сравнение двух входных значений в форме проверки с помощью AngularJS

Я пытаюсь выполнить проверку формы с помощью AngularJS. Меня особенно интересует сравнение двух значений. Я хочу, чтобы пользователь подтвердил некоторые данные, которые он ввел до его продолжения. Допустим, у меня есть код ниже:

<p>
    Email:<input type="email" name="email1" ng-model="emailReg">
    Repeat Email:<input type="email" name="email2" ng-model="emailReg2">
<p>

а затем я могу использовать валидацию с помощью:

<span ng-show="registerForm.email1.$error.required">Required!</span>
<span ng-show="registerForm.email1.$error.email">Not valid email!</span>
<span ng-show="emailReg !== emailReg2">Emails have to match!</span>  <-- see this line

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

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

Любая помощь приветствуется, спасибо

4b9b3361

Ответ 1

Один из способов добиться этого - с помощью специальной директивы. Здесь пример использования пользовательской директивы (ng-match в этом случае):

<p>Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-match="emailReg"></p>

<span data-ng-show="myForm.emailReg2.$error.match">Emails have to match!</span>

ПРИМЕЧАНИЕ. Обычно не рекомендуется использовать ng- в качестве префикса для пользовательской директивы, поскольку это может противоречить официальной директиве AngularJS.

Обновить

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

HTML

<button ng-click="add()></button>
<span ng-show="IsMatch">Emails have to match!</span>

контроллер

$scope.add = function() {
  if ($scope.emailReg != $scope.emailReg2) {
    $scope.IsMatch=true;
    return false;
  }
  $scope.IsMatch=false;
}

Ответ 2

Вы можете использовать ng-pattern/regex для сравнения 2 входных значений

Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">

и подтверждение с помощью:

<span ng-show="registerForm.email2.$error.pattern">Repeat Email should have the same value with email!</span>

Ответ 3

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

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

Без дальнейших церемоний здесь упрощенная разметка:

<form novalidate="novalidate">
    <label>email</label>
    <input type="text"
        ng-model="email"
        name="email" />
    <label>email repeated</label>
    <input ng-model="emailRepeated"
        same-as="email"
        name="emailRepeated" />
</form>

И код JS:

angular.module('app', [])
    .directive('sameAs', function() {
        return {
            require: 'ngModel',
            link: function(scope, elem, attrs, ngModel) {
                ngModel.$parsers.unshift(validate);

                // Force-trigger the parsing pipeline.
                scope.$watch(attrs.sameAs, function() {
                    ngModel.$setViewValue(ngModel.$viewValue);
                });

                function validate(value) {
                    var isValid = scope.$eval(attrs.sameAs) == value;

                    ngModel.$setValidity('same-as', isValid);

                    return isValid ? value : undefined;
                }
            }
        };
    });

Директива перехватывает конвейер парсеров, чтобы получать уведомления о любых изменениях в представлении и устанавливать достоверность на основе сравнения нового значения представления и значения ссылочного поля. Этот бит прост. Трудный бит обнюхивает изменения в поле ссылки. Для этого директива устанавливает Бодрствующего на эталонное значение и сила-triggeres трубопровод синтаксического анализа, для того, чтобы получить все валидаторы снова запустить.

Если вы хотите поиграть с ним, вот моя ручка: http://codepen.io/jciolek/pen/kaKEn

Надеюсь, это поможет, Яцек

Ответ 4

Недавно я написал настраиваемую директиву, которая может быть достаточно общей для любой проверки. Он принимает функцию проверки из текущей области

module.directive('customValidator', [function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: { validateFunction: '&' },
            link: function (scope, elm, attr, ngModelCtrl) {
                ngModelCtrl.$parsers.push(function (value) {
                    var result = scope.validateFunction({ 'value': value });
                    if (result || result === false) {
                        if (result.then) {
                            result.then(function (data) {           //For promise type result object
                                ngModelCtrl.$setValidity(attr.customValidator, data);
                            }, function (error) {
                                ngModelCtrl.$setValidity(attr.customValidator, false);
                            });
                        }
                        else {
                            ngModelCtrl.$setValidity(attr.customValidator, result);
                            return result ? value : undefined;      //For boolean result return based on boolean value
                        }
                    }
                    return value;
                });
            }
        };
    }]);

Чтобы использовать его, вы делаете

<input type="email" name="email2" ng-model="emailReg2" custom-validator='emailMatch' data-validate-function='checkEmailMatch(value)'>
<span ng-show="registerForm.email2.$error.emailMatch">Emails have to match!</span>

В вашем контроллере вы можете реализовать метод, который должен возвращать true или false

$scope.checkEmailMatch=function(value) {
    return value===$scope.emailReg;
}

Преимущество состоит в том, что вам не нужно писать настраиваемую директиву для каждой пользовательской проверки.

Ответ 5

При обновлении angular до 1.3 и выше я нашел проблему с Jacek Ciolek отличным ответом:

  • Добавить данные в поле ссылки
  • Добавьте те же данные в поле с директивой по нему (это поле теперь действительно)
  • Вернитесь в поле ссылки и измените данные (поле директивы остается в силе)

Я протестировал ответ rdukeshier (обновление var modelToMatch = element.attr('sameAs') до var modelToMatch = attrs.sameAs для правильной загрузки эталонной модели), но та же проблема возникла.

Чтобы исправить это (проверено в angular 1.3 и 1.4), я адаптировал код rdukeshier и добавил наблюдателя в поле ссылки для запуска всех проверок при изменении ссылочного поля. Теперь директива выглядит следующим образом:

angular.module('app', [])
  .directive('sameAs', function () {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ctrl) {
        var modelToMatch = attrs.sameAs;      

        scope.$watch(attrs.sameAs, function() {
          ctrl.$validate();          
        })

        ctrl.$validators.match = function(modelValue, viewValue) {
          return viewValue === scope.$eval(modelToMatch);
        };
      }
   };
});

Обновлен codepen

Ответ 6

используйте ng-pattern, так что ng-valid и ng-dirty могут действовать правильно

Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">

<span ng-show="registerForm.email2.$error.pattern">Emails have to match!</span>

Ответ 7

Нет необходимости в функции или директиве. Просто сравните их $modelValue с представлением:

ng-show="formName.email.$modelValue !== formName.confirmEmail.$modelValue"

Более подробный пример:

<span ng-show="(formName.email.$modelValue !== formName.confirmEmail.$modelValue) 
                && formName.confirmEmail.$touched
                && !formName.confirmEmail.$error.required">Email does not match.</span>

Обратите внимание на, что ConfirmEmail находится за пределами ViewModel; это свойство $scope. Его не нужно подавать.

Ответ 8

Метод Генри-Нео был близок, ему просто нужны более строгие правила регулярных выражений.

<form name="emailForm">
    Email: <input type="email" name="email1" ng-model="emailReg">
    Repeat Email: <input type="email" name="email2" ng-model="emailReg2" ng-pattern="(emailReg)">
</form>

Включая правило регулярных выражений в круглых скобках, оно будет соответствовать всей строке от emailReg до emailReg2 и приведет к сбою проверки формы, потому что она не соответствует.

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

 <p ng-show="emailForm.$valid">Form Valid</p>
 <p ng-show="emailForm.email1.$error">Email not valid</p>
 <p ng-show="emailForm.email1.$valid && emailForm.email1.$error.pattern">
     Emails Do Not Match
 </p>

Ответ 9

Этот модуль хорошо работает для сравнения двух полей. Отлично работает с Angular 1.3+. Простой в использовании https://www.npmjs.com/package/angular-password

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

Ответ 10

Вот моя простая версия настраиваемой директивы валидатора:

angular.module('app')
  .directive('equalsTo', function () {
    return {
      require: 'ngModel',
      link:    function (scope, elm, attrs, ngModel) {
        scope.$watchGroup([attrs.equalsTo, () => ngModel.$modelValue], newVal => {
          ngModel.$setValidity('equalsTo', newVal[0] === newVal[1]);
        });
      }
    };
  })

Ответ 11

Ниже приведена версия angular 1.3 директивы sameAs:

angular.module('app').directive('sameAs', [function() {
  'use strict';

  return {
    require: 'ngModel',
    restrict: 'A',
    link: function(scope, element, attrs, ctrl) {
      var modelToMatch = element.attr('sameAs');      
      ctrl.$validators.match = function(modelValue, viewValue) {
        return viewValue === scope.$eval(modelToMatch);
      };
    }
  };
}]);

Ответ 12

Мина похожа на ваше решение, но я получил ее на работу. Единственное отличие - моя модель. У меня есть следующие модели в моем html-вводе:

ng-model="new.Participant.email"
ng-model="new.Participant.confirmEmail"

и в моем контроллере у меня это в $scope:

 $scope.new = {
        Participant: {}
    };

и эта строка проверки работала:

<label class="help-block" ng-show="new.Participant.email !== new.Participant.confirmEmail">Emails must match! </label>

Ответ 13

Спасибо за отличный пример @Jacek Ciolek. Для angular 1.3.x это решение ломается, когда к исходному входному значению добавляются обновления. Основываясь на этом примере для angular 1.3.x, это решение работает также с angular 1.3.x. Он связывает и наблюдает за изменениями эталонного значения.

angular.module('app', []).directive('sameAs', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope: {
      sameAs: '='
    },
    link: function(scope, elm, attr, ngModel) {
      if (!ngModel) return;

      attr.$observe('ngModel', function(value) {
        // observes changes to this ngModel
        ngModel.$validate();
      });

      scope.$watch('sameAs', function(sameAs) {
        // watches for changes from sameAs binding
        ngModel.$validate();
      });

      ngModel.$validators.sameAs = function(value) {
        return scope.sameAs == value;
      };
    }
  };
});

Вот мое перо: http://codepen.io/kvangrae/pen/BjxMWR

Ответ 14

Вы должны посмотреть на большую проблему. Как написать директивы, которые решают одну проблему. Вы должны попробовать директиву use-form-error. Помогло бы оно решить эту проблему и многие другие.

    <form name="ExampleForm">
  <label>Password</label>
  <input ng-model="password" required />
  <br>
   <label>Confirm password</label>
  <input ng-model="confirmPassword" required />
  <div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>

Пример в реальном времени jsfiddle

Ответ 15

Мне нужно сделать это только в одной форме во всем моем приложении, и я вижу директиву, такую ​​как супер-сложный для моего случая, поэтому я использую ng-patter, как некоторые, имеет точку, но имеет некоторые проблемы, когда строка имеет специальные символы, такие как .[\, это сломалось, поэтому я создаю функцию для специальных символов scape.

$scope.escapeRegExp(str) {
  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

и в представлении

<form name="ExampleForm">
  <label>Password</label>
  <input ng-model="password" required />
  <br>
   <label>Confirm password</label>
  <input ng-model="confirmPassword" required ng-pattern="escapeRegExp(password)"/>  
</form>

Ответ 16

Конечно, для очень простых сравнений вы всегда можете использовать ngMin/ngMax.

В противном случае вы можете перейти с пользовательской директивой, и не нужно делать любые $watch или $observe или $eval или этот причудливый $setValidity взад и вперед. Кроме того, нет необходимости вообще подключаться к функции postLink. Постарайтесь держаться подальше от DOM как можно больше, поскольку это против духа angular.

Просто используйте привязки жизненного цикла, которые дает вам фреймворк. Добавьте валидатор и $validate при каждом изменении. Просто как это.

app.directive('sameAs', function() {
  return {
    restrict: 'A',
    require: {
      ngModelCtrl: 'ngModel'
    },
    scope: {
      reference: '<sameAs'
    },
    bindToController: true,
    controller: function($scope) {
      var $ctrl = $scope.$ctrl;

      //add the validator to the ngModelController
      $ctrl.$onInit = function() {
        function sameAsReference (modelValue, viewValue) {
          if (!$ctrl.reference || !modelValue) { //nothing to compare
            return true;
          }
          return modelValue === $ctrl.reference;
        }
        $ctrl.ngModelCtrl.$validators.sameas = sameAsReference;
      };

      //do the check at each change
      $ctrl.$onChanges = function(changesObj) {
        $ctrl.ngModelCtrl.$validate();
      };
    },
    controllerAs: '$ctrl'
  };
});

Ваш plunker.

Ответ 17

Я модифицировал метод Chandermani для совместимости с Angularjs 1.3 и upper. Перенесено из $ parsers в $ asyncValidators.

module.directive('customValidator', [function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        scope: { validateFunction: '&' },
        link: function (scope, elm, attr, ngModelCtrl) {
            ngModelCtrl.$asyncValidators[attr.customValidator] = function (modelValue, viewValue) {
                return new Promise(function (resolve, reject) {
                    var result = scope.validateFunction({ 'value': viewValue });
                    if (result || result === false) {
                        if (result.then) {
                            result.then(function (data) {           //For promise type result object
                                if (data)
                                    resolve();
                                else
                                    reject();
                            }, function (error) {
                                reject();
                            });
                        }
                        else {
                            if (result)
                                resolve();
                            else
                                reject();
                            return;
                        }
                    }
                    reject();
                });
            }

        }
    };
}]);

Использование одинаковое