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

时间:2014-03-04 13:16:17

标签: forms angularjs validation

我正在尝试使用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 :(得分:46)

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

答案 1 :(得分:37)

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

我希望它有所帮助, 亚切克

答案 3 :(得分:12)

我最近写了一个自定义指令,它足够通用,可以进行任何验证。它采用当前范围的验证函数

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

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

答案 4 :(得分:8)

将角度升级到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

答案 5 :(得分:5)

使用ng-pattern,以便ng-valid和ng-dirty可以正常运行

.to_csv()

答案 6 :(得分:4)

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

请注意,确认电子邮件在ViewModel之外;它是$ scope的属性。它不需要提交。

答案 7 :(得分:1)

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

通过在括号中包含正则表达式规则,它会将整个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>

答案 8 :(得分:1)

以下是我的自定义验证程序指令的简单版本:

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

答案 9 :(得分:1)

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

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

答案 10 :(得分:0)

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

        }
    };
}]);

用法相同

答案 11 :(得分:0)

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

否则,您可以使用自定义指令,无需执行任何$watch$observe$eval或此幻想{{ 1}}来回。此外,根本不需要挂钩 postLink 功能。尽量避免使用DOM,因为它违背了棱角分明的精神。

只需使用框架为您提供的生命周期钩子即可。每次更改时都添加验证器$setValidity。就这么简单。

$validate

您的plunker

答案 12 :(得分:0)

我需要在我的整个应用程序中以一种形式执行此操作,并且我看到一个类似于我的情况的超级复杂的指令,所以我使用ng-patter就像有些有点,但有一些问题,当string有像.[\这样的特殊字符,所以我创建了一个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>

答案 13 :(得分: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

答案 14 :(得分:0)

感谢好的例子@Jacek Ciolek。对于角度1.3.x,当对参考输入值进行更新时,此解决方案会中断。在角度为1.3.x的此示例的基础上,此解决方案与Angular 1.3.x一样可行。它绑定并监视参考值的变化。

.content-outer { 
    max-width: 100% !important; 
}

这是我的笔:http://codepen.io/kvangrae/pen/BjxMWR

答案 15 :(得分:0)

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

答案 16 :(得分: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);
      };
    }
  };
}]);