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

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

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

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

实际上,将控制器中的字段标记为无效(同时保持FormController同步)可能是我在最简单的场景中完成工作所需的东西,但我不知道如何做到这一点。

315880 次浏览

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.example','verybad@domain.example'];
$scope.notBlackListed = function(value) {
return $scope.blackList.indexOf(value) === -1;
};
}

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

标准表单验证消息(1.0.;X及以上)

因为这是你谷歌“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,最好用ng-if代替上面的ng-show

这里是一个强制的砰砰作响的链接

此外,我还写了一些关于这个主题的博客文章,内容更详细一些:

Angular Form Validation

自定义验证指令 .

编辑:在1.3.X中使用ngMessages

现在你可以使用ngMessages模块来代替ngShow来显示错误消息。它实际上可以与任何东西一起工作,它不一定是一个错误消息,但这里是基本的:

  1. 包括<script src="angular-messages.js"></script>
  2. 在模块声明中引用ngMessages:

    var app = angular.module('myApp', ['ngMessages']);
    
  3. Add the appropriate markup:

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

In the above markup, ng-message="personForm.email.$error" basically specifies a context for the ng-message child directives. Then ng-message="required" and ng-message="email" specify properties on that context to watch. Most importantly, they also specify an order to check them in. The first one it finds in the list that is "truthy" wins, and it will show that message and none of the others.

And a plunker for the ngMessages example

你可以在你的验证场景中使用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>

@ synergy我认为@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);

下面是在表单(from: 使用AngularJS和过滤器进行高级表单验证)中进行自定义通配符表达式验证的一种很酷的方法:

<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

你可以使用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的作者

更新:

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

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

ng-model或任何ensure-watch变量被改变时,执行ensure-expression来验证模型。

我最近创建了一个指令,允许基于表达式的角表单输入失效。任何有效的角表达式都可以使用,并且它支持使用对象表示法自定义验证键。用angular 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 " invalididif ")

<input ng-model="foo" invalid-if="foo > bar"/>
在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是内置函数,用于设置模型状态为有效/无效

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

< p >使用:

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

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

在这篇文章中有一些很好的例子和注解,但它们并不是我想要的。我的方法:angular-validity——一个基于承诺的异步验证库,可选的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上可用,有详细的文档和大量的现场演示。

调用服务器的自定义验证

使用处理异步验证的ngModelController $asyncValidators API,例如向后端发出$http请求。添加到对象中的函数必须返回一个promise,该promise在有效时必须被解析,在无效时必须被拒绝。正在进行的异步验证按键存储在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;
});
};

有关更多信息,请参见


使用$validators API

接受的答案使用$parsers$formatters管道添加一个自定义同步验证器。AngularJS 1.3+添加了一个$validators API,因此不需要在$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