【问题标题】:Comparing two input values in a form validation with AngularJS使用 AngularJS 比较表单验证中的两个输入值
【发布时间】:2014-04-06 01:13:09
【问题描述】:

我正在尝试使用 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 将对输入中的文本做出正确反应,除非我不知道如何在此验证中使用比较来强制电子邮件相同,然后才允许用户提交表单。

我希望有一个没有自定义指令的解决方案,但如果没有它就无法实现,我会处理它。 Here 是使用自定义指令解决类似问题的答案。

感谢您的帮助,谢谢

【问题讨论】:

  • 提交按钮上的ng-disabled="emailReg != emailReg2"怎么样?

标签: forms angularjs validation


【解决方案1】:

您应该能够使用 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>

【讨论】:

  • 在以下情况下不起作用:email:abc,email2:abcd
  • 它应该可以工作,但如果您将“abc”或“abcd”作为输入,这些不是有效的电子邮件地址,可能在进入模式验证之前验证失败。
  • 这太棒了。已经实现了一个似乎每个人都建议的自定义指令,但我完全放弃了它,因为这对我来说更符合“Angular 方式”的做事方式!
  • 当然,当你有正则表达式符号时,这会失败。我也希望“test@test.com”与“test@testacom”匹配,所以这是一种非常糟糕的方法。 - /test@test.com/.test('test@testacom')
  • @Sam ,我通过“转义”输入避免了这个问题,因此它的正则表达式符号被解释为文字匹配。 stackoverflow.com/questions/494035/…
【解决方案2】:

实现此目的的一种方法是使用自定义指令。这是一个使用自定义指令的示例(在本例中为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;
}

【讨论】:

  • 感谢您的回答。所以我必须使用指令才能工作......到目前为止可能没有其他方法吗?
  • @Why 指令?你可以简单地使用我的代码。很容易接力。否则你需要手动编写一些代码
  • 谢谢,这很好 - 我仍然可以从您链接的页面复制指令,它也可以正常工作,所以我稍后会看看什么最有效。
  • ng-match 不是 AngularJS 框架提供的验证指令。请不要使用 ng-* 命名您自己的自定义指令。现有验证器的有效列表位于docs.angularjs.org/api/ng/directive/input
  • ng-match 的链接错误
【解决方案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;
                }
            }
        };
    });

该指令挂钩到解析器管道,以便根据新视图值和参考字段值的比较来获得视图值的任何更改并设置有效性的通知。那一点很容易。棘手的一点是嗅探参考字段的变化。为此,该指令在参考值上设置了一个观察者并强制触发解析管道,以使所有验证器再次运行。

如果你想玩它,这是我的笔: http://codepen.io/jciolek/pen/kaKEn

希望对你有帮助 杰克

【讨论】:

  • 在 Angular 1.5.0 上,ngModel.$setViewValue(ngModel.$viewValue); 不会导致 $parsers 运行。我不得不改用validate(ngModel.$viewValue);
  • 这对我有用,但是,为了输出自定义错误,我必须将验证更改为 'sameas' 而不是 'same-as' 以便我可以使用 'myform.emailaddress2.$ error.sameas'
【解决方案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's great answer 时出现问题:

  • 向参考字段添加数据
  • 将相同的数据添加到带有指令的字段(该字段现在有效)
  • 返回参考字段并更改数据(指令字段保持有效)

我测试了rdukeshier's answer(将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);
        };
      }
   };
});

Updated codepen

【讨论】:

  • 这是 1.4 及更高版本所必需的。谢谢!
【解决方案6】:

不需要函数或指令。只需从视图中比较它们的 $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 的属性。不需要提交。

【讨论】:

  • 更好的解决方案!
【解决方案7】:

使用 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>

【讨论】:

    【解决方案8】:

    @Henry-Neo 的方法很接近,只是需要更严格的 Regex 规则。

    <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>
    

    通过在括号内包含正则表达式规则,它将匹配emailRegemailReg2 的整个字符串,并会导致表单验证失败,因为它不匹配。

    然后您可以深入研究元素以找出失败的部分。

     <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>
    

    【讨论】:

    • 如果 emailReg 是 .* 这不起作用,那么您可以输入任何内容作为 emailReg2。
    【解决方案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]);
              });
            }
          };
        })
      

      【讨论】:

      • 这是此页面上唯一适用于 Angular 1.6.4 的解决方案。谢谢!
      【解决方案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 及更高版本。从 $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();
                                  });
                              }
                  
                          }
                      };
                  }]);
                  

                  用法一样

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-11-07
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多