如何将自定义validation添加到AngularJS窗体?

我有一个表单,input字段和validation设置,通过添加required属性等。 但对于一些领域,我需要做一些额外的validation。 我怎么“点击”到FormController控制的validation?

自定义validation可能类似于“如果这3个字段被填充,那么这个字段是必需的,需要以特定的方式格式化”。

FormController.$setValidity有一个方法,但是看起来不像公共API,所以我宁愿不使用它。 创build一个自定义指令,并使用NgModelController看起来像另一种select,但基本上需要我为每个自定义validation规则,我不想要的指令。

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

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

标准表单validation消息(1.0.X及以上)

因为如果你是Google的“Angular Form Validation”,这是最好的结果之一,所以我现在想为从这里进来的任何人添加另一个答案。

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

这是“公开”,无后顾之忧。 用它。 这就是它的目的。 如果它不打算被使用,angular开发商将在closures私有化。

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

 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中,可能最好使用ng-if ng-show来replace上面的ng-show

这是一个强制性的链接

另外,我已经写了一些关于这个主题的博客文章,详细介绍一下:

有效的表单validation

自定义validation指令

编辑:使用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"指定要监视的上下文的属性。 最重要的是,他们还指定了一个命令来检查他们 。 第一个在列表中发现的“truthy”获胜,它将显示该消息,而不是其他消息。

ngMessages例子的一个重要的东西

Angular-UI的项目包括一个ui-validate指令,这可能会帮助你。 让我们来指定一个函数来进行validation。

看看演示页面: http : //angular-ui.github.com/ ,search到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; }; } 

你可以在你的validation场景中使用ng-required(“如果这三个字段被填入,那么这个字段是必需的”:

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

你可以使用Angular-Validator 。

例如:使用一个函数来validation一个字段

 <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的字段2和字段3是范围variables,您可能还想检查字段是否不等于空string)

如果该字段没有通过validator那么该字段将被标记为无效,并且用户将不能提交该表格。

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

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

这里有一个很酷的方法来在表单中进行自定义通配符expression式validation(来自: 使用AngularJS和filter进行高级表单validation ):

 <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演示 (支持expression式命名和多个expression式)

它和ui-validate类似,但是你不需要一个范围特定的validation函数(这个工作一般),而且你不需要这样的ui.utils

我最近创build了一个指令,允许angular度表单input的基于expression式的无效。 可以使用任何有效的angular度expression式,并使用对象表示法支持自定义validation键。 testingangular度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()'}/> 

或者只是传入一个expression式(它将被赋予“invalidIf”的默认validationKey)

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

@synergetic我想@blesh假设把functionvalidation如下

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

更新:

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

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

结果:在其他指令模型和当前模型的更改上执行validation器的相互依赖的testingexpression式。

testingexpression式有本地的$modelvariables,你应该用它来比较它与其他variables。

先前:

我已经尝试通过添加额外的指令来改进@Plantface代码。 这个额外的指令非常有用,如果我们的expression式需要在多个ngModelvariables中进行更改时执行。

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

示例如何使用它来进行交叉validation字段:

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

ng-model或任何ensure-watchvariables被改变时,执行ensure-expression来validation模型。

在AngularJS中定义自定义validation的最佳位置是Cutsom指令。 AngularJS提供了一个ngMessages模块。

ngMessages是一个指令,它被devise成根据它监听的键/值对象的状态来显示和隐藏消息。 指令本身补充了使用ngModel $ error对象(存储validation错误的键/值状态)的错误消息报告。

对于自定义表单validation一个应该使用ngMessages模块与自定义指令。在这里我有一个简单的validation,将检查数字长度是否less于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> 

以下是如何创build自定义validation指令

 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是内置函数,用于将模型状态设置为有效/无效

我扩展了@Ben Lesh的答案,能够指定validation是否区分大小写(默认)

使用:

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

在这个线程中提供了一些很好的例子和库,但是他们并没有find我想要的东西。 我的方法: angular度有效性 – 一个基于承诺的asynchronousvalidation的validation库,可选的Bootstrap样式烘焙。

OP的用例的angular度有效性解决scheme可能如下所示:

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

这是一个小提琴 ,如果你想要旋转。 该库在GitHub上提供 ,有详细的文档和大量的现场演示。

调用服务器的自定义validation

使用处理asynchronousvalidation的ngModelController $asyncValidators API ,例如向后端发出$http请求。 添加到对象的函数必须返回一个有效时必须parsing的promise,否则返回invalid。 正在进行的asynchronousvalidation通过键存储在ngModelController.$pending 。 有关更多信息,请参阅AngularJS开发人员指南 – 表单(自定义validation) 。

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

有关更多信息,请参阅

  • ngModelController $asyncValidators API

  • AngularJS开发人员指南 – 表单(自定义validation) 。