如何将自定义验证添加到AngularJS表单?


278

我有一个带有输入字段和通过添加required属性等进行验证设置的表单。但是对于某些领域,我需要做一些额外的验证。我将如何“利用” FormController控制的验证?

自定义验证可能类似于“如果填写了这3个字段,那么此字段是必需的,并且需要以特定方式进行格式化”。

有一个方法,FormController.$setValidity但是看起来不像公共API,所以我宁愿不使用它。创建自定义指令并使用它NgModelController看起来像另一个选项,但是基本上需要我为每个自定义验证规则创建一个指令,这是我所不希望的。

实际上,将控制器中的字段标记为无效(同时保持FormController同步)可能是我在最简单的情况下完成工作所需要的,但是我不知道该怎么做。


4
有一篇不错的文章介绍了如何编码怪物以处理有角JS中的自定义验证。检查
安舒

这不是我想要的,因为它需要自定义指令,但是我还是会接受您的回答,因为无论如何这是一篇好文章。
botteaap 2012年

我想知道同样的事情,我很喜欢FormController级别的一些控件。例如,我希望某些自定义指令将FormController实例标记为formName.$warning
亚当·瓦瑟尔努克

2
我相信$$在公开API之前,它$是公开的。见stackoverflow.com/questions/19338493/...
丹尼尔˚F

Answers:


370

编辑:在下面添加了有关ngMessages(> = 1.3.X)的信息。

标准表单验证消息(1.0.X及更高版本)

由于如果您使用Google“ Angular Form Validation”,这是最好的结果之一,所以我想为从那里来的任何人添加另一个答案。

FormController。$ setValidity中有一个方法,但是它看起来不像公共API,所以我宁愿不使用它。

这是“公开”的,不用担心。用它。那就是它的目的。如果不打算使用它,Angular开发人员将把它私有化。

要进行自定义验证,如果您不想使用Angular-UI作为其他建议的答案,则可以简单地滚动自己的验证指令。

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
          var blacklist = attr.blacklist.split(',');

          //For DOM -> model validation
          ngModel.$parsers.unshift(function(value) {
             var valid = blacklist.indexOf(value) === -1;
             ngModel.$setValidity('blacklist', valid);
             return valid ? value : undefined;
          });

          //For model -> DOM validation
          ngModel.$formatters.unshift(function(value) {
             ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
             return value;
          });
      }
   };
});

这是一些用法示例:

<form name="myForm" ng-submit="doSomething()">
   <input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
   <span ng-show="myForm.fruitName.$error.blacklist">
      The phrase "{{data.fruitName}}" is blacklisted</span>
   <span ng-show="myForm.fruitName.$error.required">required</span>
   <button type="submit" ng-disabled="myForm.$invalid">Submit</button>
</form>

注:在1.2.X它可能preferrable替换ng-ifng-show上述

这是一个强制性的链接

另外,我已经写了一些关于该主题的博客条目,其中涉及了更多细节:

角度形式验证

自定义验证指令

编辑:在1.3.X中使用ngMessages

现在,您可以使用ngMessages模块而不是ngShow来显示错误消息。它实际上可以与任何东西一起使用,它不一定是错误消息,但是这里是基础知识:

  1. 包括 <script src="angular-messages.js"></script>
  2. ngMessages模块声明中的参考:

    var app = angular.module('myApp', ['ngMessages']);
  3. 添加适当的标记:

    <form name="personForm">
      <input type="email" name="email" ng-model="person.email" required/>
    
      <div ng-messages="personForm.email.$error">
        <div ng-message="required">required</div>
        <div ng-message="email">invalid email</div>
      </div>
    </form>

在上面的标记中,ng-message="personForm.email.$error"基本上为ng-message子指令指定了上下文。然后 ng-message="required"ng-message="email"在该上下文上指定要监视的属性。最重要的是,它们还指定了检入它们的顺序。它在列表中找到的第一个“真实”获胜,它将显示该消息,而没有其他消息。

还有一个ngMessages示例的插件


6
如果在传递给$ parsers.unshift的函数上返回值,错误的值也将被保存到模型中-最好返回未定义的值(当值无效时)。
georgiosd

5
+1 @georgiosd ... 100%正确。查看Angular所做的事情,他们返回的是未定义的。这可能不是一个交易,返回值,如(希望)从无效形式的模型没有提交...但防患于未然,我想。
Ben Lesh 2013年

2
好东西!如果您在这里用Google搜索的方式寻找有关Angular中自定义验证的良好文章,请查看@blesh编写的内容
maaachine 2013年

您是否使用AngularJS和过滤器检查了高级表单验证?它一般解决了过滤器验证问题。
Benny Bottema 2014年

1
我认为您可能打算这样做return value ? valid : undefined
GChorn 2015年

92

Angular-UI的项目包括一个ui-validate指令,这可能会帮助您。它使您可以指定一个函数来进行验证。

看一下演示页面:http : //angular-ui.github.com/,向下搜索Validate标题。

从演示页面:

<input ng-model="email" ui-validate='{blacklist : notBlackListed}'>
<span ng-show='form.email.$error.blacklist'>This e-mail is black-listed!</span>

然后在您的控制器中:

function ValidateCtrl($scope) {
  $scope.blackList = ['bad@domain.com','verybad@domain.com'];
  $scope.notBlackListed = function(value) {
    return $scope.blackList.indexOf(value) === -1;
  };
}

使用Angular 1.4不能正常工作对我来说是多么奇怪
Nick

46

您可以将ng-required用于验证方案(“如果填写了这3个字段,则此字段为必填”:

<div ng-app>
    <input type="text" ng-model="field1" placeholder="Field1">
    <input type="text" ng-model="field2" placeholder="Field2">
    <input type="text" ng-model="field3" placeholder="Field3">
    <input type="text" ng-model="dependentField" placeholder="Custom validation"
        ng-required="field1 && field2 && field3">
</div>

2
这对我有用。对于依赖于其他字段的值简单的验证,这是去代替书面方式复杂验证规则的方式
VimalKumar

28

您可以使用Angular-Validator

示例:使用函数来验证字段

<input  type = "text"
    name = "firstName"
    ng-model = "person.firstName"
    validator = "myCustomValidationFunction(form.firstName)">

然后在您的控制器中,您将看到

$scope.myCustomValidationFunction = function(firstName){ 
   if ( firstName === "John") {
       return true;
    }

您还可以执行以下操作:

<input  type = "text"
        name = "firstName"
        ng-model = "person.firstName"
        validator = "'!(field1 && field2 && field3)'"
        invalid-message = "'This field is required'">

(其中field1 field2和field3是范围变量。您可能还想检查这些字段是否不等于空字符串)

如果该字段未通过,validator则该字段将被标记为无效,并且用户将无法提交表单。

有关更多用例和示例,请参见:https : //github.com/turinggroup/angular-validator

免责声明:我是Angular-Validator的作者


13

我最近创建了一个指令,以允许基于表达式的角度形式输入无效。可以使用任何有效的角度表达式,并且它支持使用对象表示法的自定义验证键。经角度v1.3.8测试

        .directive('invalidIf', [function () {
        return {
            require: 'ngModel',
            link: function (scope, elm, attrs, ctrl) {

                var argsObject = scope.$eval(attrs.invalidIf);

                if (!angular.isObject(argsObject)) {
                    argsObject = { invalidIf: attrs.invalidIf };
                }

                for (var validationKey in argsObject) {
                    scope.$watch(argsObject[validationKey], function (newVal) {
                        ctrl.$setValidity(validationKey, !newVal);
                    });
                }
            }
        };
    }]);

您可以像这样使用它:

<input ng-model="foo" invalid-if="{fooIsGreaterThanBar: 'foo > bar',
                                   fooEqualsSomeFuncResult: 'foo == someFuncResult()'}/>

或仅通过传递表达式(将为其提供默认的validationKey“ invalidIf”)

<input ng-model="foo" invalid-if="foo > bar"/>

13

这是在表单中进行自定义通配符表达式验证的一种很酷的方法(来自:使用AngularJS和filter的高级表单验证):

<form novalidate="">  
   <input type="text" id="name" name="name" ng-model="newPerson.name"
      ensure-expression="(persons | filter:{name: newPerson.name}:true).length !== 1">
   <!-- or in your case:-->
   <input type="text" id="fruitName" name="fruitName" ng-model="data.fruitName"
      ensure-expression="(blacklist | filter:{fruitName: data.fruitName}:true).length !== 1">
</form>
app.directive('ensureExpression', ['$http', '$parse', function($http, $parse) {
    return {
        require: 'ngModel',
        link: function(scope, ele, attrs, ngModelController) {
            scope.$watch(attrs.ngModel, function(value) {
                var booleanResult = $parse(attrs.ensureExpression)(scope);
                ngModelController.$setValidity('expression', booleanResult);
            });
        }
    };
}]);

jsFiddle演示(支持表达式命名和多个表达式)

它与相似ui-validate,但是您不需要范围特定的验证功能(此功能通常适用),当然您不需要ui.utils这样。


谢谢。很酷。将验证规则应用于动态表单特别有用。但是,即使无效,它仍会设置模型值。无论如何要防止它设置为无效的modelValue?
YuMei

5

更新:

具有相同功能的先前指令的改进和简化版本(一个而不是两个):

.directive('myTestExpression', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            var expr = attrs.myTestExpression;
            var watches = attrs.myTestExpressionWatch;

            ctrl.$validators.mytestexpression = function (modelValue, viewValue) {
                return expr == undefined || (angular.isString(expr) && expr.length < 1) || $parse(expr)(scope, { $model: modelValue, $view: viewValue }) === true;
            };

            if (angular.isString(watches)) {
                angular.forEach(watches.split(",").filter(function (n) { return !!n; }), function (n) {
                    scope.$watch(n, function () {
                        ctrl.$validate();
                    });
                });
            }
        }
    };
}])

用法示例:

<input ng-model="price1" 
       my-test-expression="$model > 0" 
       my-test-expression-watch="price2,someOtherWatchedPrice" />
<input ng-model="price2" 
       my-test-expression="$model > 10" 
       my-test-expression-watch="price1" 
       required />

结果:相互依赖的测试表达式,其中在更改其他指令模型和当前模型时执行验证器。

测试表达式具有局部$model变量,应将其与其他变量进行比较。

先前:

我已经尝试通过添加额外的指令来改进@Plantface代码。如果对多个ngModel变量进行更改时需要执行我们的表达式,则此额外指令非常有用。

.directive('ensureExpression', ['$parse', function($parse) {
    return {
        restrict: 'A',
        require: 'ngModel',
        controller: function () { },
        scope: true,
        link: function (scope, element, attrs, ngModelCtrl) {
            scope.validate = function () {
                var booleanResult = $parse(attrs.ensureExpression)(scope);
                ngModelCtrl.$setValidity('expression', booleanResult);
            };

            scope.$watch(attrs.ngModel, function(value) {
                scope.validate();
            });
        }
    };
}])

.directive('ensureWatch', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: 'ensureExpression',
        link: function (scope, element, attrs, ctrl) {
            angular.forEach(attrs.ensureWatch.split(",").filter(function (n) { return !!n; }), function (n) {
                scope.$watch(n, function () {
                    scope.validate();
                });
            });
        }
    };
}])

示例如何使用它制作交叉验证的字段:

<input name="price1"
       ng-model="price1" 
       ensure-expression="price1 > price2" 
       ensure-watch="price2" />
<input name="price2" 
       ng-model="price2" 
       ensure-expression="price2 > price3" 
       ensure-watch="price3" />
<input name="price3" 
       ng-model="price3" 
       ensure-expression="price3 > price1 && price3 > price2" 
       ensure-watch="price1,price2" />

ensure-expression当变量ng-modelensure-watch变量被更改时,执行来验证模型。


4

@synergetic我认为@blesh应该将功能验证如下

function validate(value) {
    var valid = blacklist.indexOf(value) === -1;
    ngModel.$setValidity('blacklist', valid);
    return valid ? value : undefined;
}

ngModel.$formatters.unshift(validate);
ngModel.$parsers.unshift(validate);

4

调用服务器的自定义验证

使用ngModelController $asyncValidatorsAPI来处理异步验证,例如$http向后端发出请求。添加到对象的函数必须返回一个承诺,该承诺必须在有效时解决,在无效时被拒绝。进行中的异步验证通过键入来存储ngModelController.$pending。有关更多信息,请参见AngularJS开发人员指南-表单(自定义验证)

ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
  var value = modelValue || viewValue;

  // Lookup user by username
  return $http.get('/api/users/' + value).
     then(function resolved() {
       //username exists, this means validation fails
       return $q.reject('exists');
     }, function rejected() {
       //username does not exist, therefore this validation passes
       return true;
     });
};

有关更多信息,请参见


使用$validatorsAPI

接受的答案使用$parsers$formatters管道添加自定义同步验证器。AngularJS 1.3+添加了一个$validatorsAPI,因此无需将验证器放入$parsers和中$formatters管道中:

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {           
          ngModel.$validators.blacklist = function(modelValue, viewValue) {
              var blacklist = attr.blacklist.split(',');
              var value = modelValue || viewValue;
              var valid = blacklist.indexOf(value) === -1;
              return valid;
          });    
      }
   };
});

有关更多信息,请参见AngularJS ngModelController API参考-$ validators


3

在AngularJS中,定义自定义验证的最佳位置是Cutsom指令。AngularJS提供了ngMessages模块。

ngMessages是一种指令,旨在根据其侦听的键/值对象的状态显示和隐藏消息。指令本身通过ngModel $ error对象(存储验证错误的键/值状态)来补充错误消息报告。

对于自定义表单验证,应该使用带有自定义指令的ngMessages模块。这里我有一个简单的验证,它将检查数字长度是否小于6,从而在屏幕上显示错误

 <form name="myform" novalidate>
                <table>
                    <tr>
                        <td><input name='test' type='text' required  ng-model='test' custom-validation></td>
                        <td ng-messages="myform.test.$error"><span ng-message="invalidshrt">Too Short</span></td>
                    </tr>
                </table>
            </form>

这是创建自定义验证指令的方法

angular.module('myApp',['ngMessages']);
        angular.module('myApp',['ngMessages']).directive('customValidation',function(){
            return{
            restrict:'A',
            require: 'ngModel',
            link:function (scope, element, attr, ctrl) {// 4th argument contain model information 

            function validationError(value) // you can use any function and parameter name 
                {
                 if (value.length > 6) // if model length is greater then 6 it is valide state
                 {
                 ctrl.$setValidity('invalidshrt',true);
                 }
                 else
                 {
                 ctrl.$setValidity('invalidshrt',false) //if less then 6 is invalide
                 }

                 return value; //return to display  error 
                }
                ctrl.$parsers.push(validationError); //parsers change how view values will be saved in the model
            }
            };
        });

$setValidity 是内置函数,用于将模型状态设置为有效/无效


1

我扩展了@Ben Lesh的答案,可以指定验证是否区分大小写(默认)

用:

<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="Coconuts,Bananas,Pears" caseSensitive="true" required/>

码:

angular.module('crm.directives', []).
directive('blacklist', [
    function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                'blacklist': '=',
            },
            link: function ($scope, $elem, $attrs, modelCtrl) {

                var check = function (value) {
                    if (!$attrs.casesensitive) {
                        value = (value && value.toUpperCase) ? value.toUpperCase() : value;

                        $scope.blacklist = _.map($scope.blacklist, function (item) {
                            return (item.toUpperCase) ? item.toUpperCase() : item
                        })
                    }

                    return !_.isArray($scope.blacklist) || $scope.blacklist.indexOf(value) === -1;
                }

                //For DOM -> model validation
                modelCtrl.$parsers.unshift(function (value) {
                    var valid = check(value);
                    modelCtrl.$setValidity('blacklist', valid);

                    return value;
                });
                //For model -> DOM validation
                modelCtrl.$formatters.unshift(function (value) {
                    modelCtrl.$setValidity('blacklist', check(value));
                    return value;
                });
            }
        };
    }
]);

0

该线程中提供了一些很棒的示例和库,但是它们并没有我想要的。我的方法:angular-validity-用于异步验证的基于promise的验证库,带有可选的Bootstrap样式。

OP用例的角度有效性解决方案可能看起来像这样:

<input  type="text" name="field4" ng-model="field4"
        validity="eval"
        validity-eval="!(field1 && field2 && field3 && !field4)"
        validity-message-eval="This field is required">

这是一个小提琴,如果您想试一试。该库位于GitHub上,具有详细的文档和大量的实时演示。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.