使用AngularJS进行表单验证时,比较两个输入值。

48

我正在使用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将根据输入框中的文本正确响应,但我不知道如何在此验证中使用比较以强制要求在允许用户提交表单之前必须输入相同的电子邮件地址。

我希望能够找到一种不需要自定义指令的解决方案,但如果无法实现,则将处理它。 这里提供了一个解决类似问题的自定义指令答案。

感谢任何帮助,谢谢


6
提交按钮上加上ng-disabled="emailReg != emailReg2"怎么样? - AlwaysALearner
17个回答

46
你应该能够使用ng-pattern/正则表达式来比较两个输入值。

你应该能够使用 ng-pattern/ 正则表达式来比较 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>

在以下情况下它将无法工作:电子邮件:abc,电子邮件2:abcd。 - Manto
2
这太棒了。已经实现了一个自定义指令,这是每个人都建议的,但我完全放弃它,因为这对我来说更符合“Angular方式”做事的感觉! - Boris
3
当使用正则表达式的符号时,这种方法就会失败。而且我期望"test@test.com"可以匹配到"test@testacom",所以这是一种相当糟糕的方法。- /test@test.com/.test('test@testacom') - Sam
1
@Sam,我通过“转义”输入来避免了这个问题,因此它的正则表达式符号被解释为字面匹配。https://dev59.com/rnRB5IYBdhLWcg3w1Kr0#494122 - Nate Anderson
这些其他的问题/答案与ng-pattern有关; 如何使用ng-pattern在AngularJS中验证电子邮件IDAngularJS动态ng-pattern验证 - Nate Anderson
显示剩余2条评论

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

谢谢您的回答。所以我必须使用指令才能使其工作……可能目前没有其他方法吧? - trainoasis
1
为什么要使用@Why指令?你可以直接使用我的代码,非常简单。否则你需要手动编写一些代码。 - Ramesh Rajendran
1
谢谢,这很好 - 我仍然可以从您链接的页面复制指令并且它也能正常工作,所以我稍后会看看哪个更好。 - trainoasis
18
ng-match不是由AngularJS框架提供的验证指令。请不要以ng-*命名自己的自定义指令。现有验证器的有效列表可在https://docs.angularjs.org/api/ng/directive/input上找到。 - Ryan Kimber
1
ng-match的链接错误。 - drogon
显示剩余2条评论

29

trainosais - 您是正确的,验证应该在指令级别进行。这很干净、模块化并允许代码的可重用性。当您在控制器中拥有基本验证时,您必须为不同的表单一遍又一遍地编写它。这非常反DRY。

我最近遇到了类似的问题,并通过一个简单的指令解决了它,该指令插入到解析器管道中,因此与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
在Angular 1.5.0中,ngModel.$setViewValue(ngModel.$viewValue);不会触发$parsers。我不得不使用validate(ngModel.$viewValue);来代替。 - Zane
这对我起作用了,但是为了输出自定义错误,我不得不将验证更改为“sameas”而不是“same-as”,以便我可以使用“myform.emailaddress2.$error.sameas”。 - Code Pharaoh

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

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


抱歉,这需要修复 :(. 我将其发布为验证库,请查看此博客 http://blog.technovert.com/2014/03/angularjs-form-validation-library-directives/ 和源代码 https://github.com/technovert/angular-validation - Chandermani

8
当将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代码


这对于1.4及以上版本是必要的。谢谢! - user1916988

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的属性。它不需要提交。

更好的解决方案! - Ben Rondeau

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

2

@Henry-Neo的方法接近正确,只需要更加严格的正则表达式规则。

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

2
如果emailReg是.*,那么你可以输入任何内容作为emailReg2,这样就行不通了。 - Laszlo B

1

这个模块非常适合比较两个字段。在Angular 1.3+中表现出色。使用简单。 https://www.npmjs.com/package/angular-password

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


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

这是在Angular 1.6.4上对我有效的此页面唯一的解决方案。谢谢! - Brad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接