首页 文章

使用AngularJS比较表单验证中的两个输入值

提问于
浏览
47

我正在尝试使用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是一个解决自定义指令类似问题的答案 .

任何帮助表示感谢,谢谢

17 回答

  • 0

    你必须看看更大的问题 . 如何编写解决一个问题的指令 . 你应该尝试指令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

  • 8

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

    Controller

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

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

    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

    我希望它有所帮助,Jacek

  • 1

    我最近编写了一个自定义指令,它可以通用,可以进行任何验证 . 它从当前范围获取验证功能

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

    优点是您不必为每个自定义验证编写自定义指令 .

  • 0

    我需要在我的整个应用程序中以一种形式执行此操作,并且我看到一个类似于我的情况的超级复杂的指令,所以我使用 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>
    
  • 46

    将角度升级到1.3及以上时,我发现使用Jacek Ciolek's great answer的问题:

    • 将数据添加到参考字段

    • 将相同的数据添加到带有指令的字段中(此字段现在有效)

    • 返回参考字段并更改数据(指令字段保持有效)

    我测试了rdukeshier's answer(更新 var modelToMatch = element.attr('sameAs')var modelToMatch = attrs.sameAs 以正确检索参考模型)但是出现了同样的问题 .

    为了解决这个问题(在角度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

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

    不需要功能或指令 . 只需从视图中比较他们的$ 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>
    

    Please note ,确认电子邮件在ViewModel之外;它需要提交's property of the $scope. It doesn' .

  • 4

    @ 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>
    
  • 37

    该模块适用于比较两个字段 . 适用于Angular 1.3 . 简单易用https://www.npmjs.com/package/angular-password

    它还允许将模块保存为通用模块 . 只需将它们包含在模块的包列表中即可 .

  • 12

    这是我的自定义验证器指令的简单版本:

    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]);
            });
          }
        };
      })
    
  • 0

    这是sameAs指令的1.3版本角度:

    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);
          };
        }
      };
    }]);
    
  • 5

    我的解决方案与你的解决方案类似,但我得到了它的工作 . 唯一不同的是我的模型 . 我的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>
    
  • 0

    感谢伟大的例子@Jacek Ciolek . 对于角度1.3.x,当对参考输入值进行更新时,此解决方案会中断 . 在角度为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

  • 0

    当然,对于非常简单的比较,您始终可以使用 ngMin / ngMax .

    否则,您可以使用自定义指令,并且 no need 可以来回执行任何 $watch$observe$eval 或这个花哨的 $setValidity . 此外,根本不需要挂钩到postLink功能 . 尝试尽可能地远离DOM,因为它违背了棱角分明的精神 .

    只需使用框架为您提供的生命周期钩子 . 在每次更改时添加验证器和 $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 .

  • 0

    我修改了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();
                    });
                }
    
            }
        };
    }]);
    

    用法是一样的

相关问题