我想动态地将指令添加到表单元素(input,select,textarea ...)
<div ng-app="app" ng-controller="ctrl">
<form form-validator>
<input type="text" ng-model="model1">
<input type="text" ng-model="model2">
<input type="text" ng-model="model3">
<textarea ng-model="model4"></textarea>
<select>
<option ng-model="model5" value="1">Name</option>
</select>
</form>
</div>
指令
var app=angular.module('app',[])
.controller('ctrl',function($scope){
})
.directive('formValidator',function($compile){
return{
restrict: 'A',
link: function(scope, elem, attrs){
//is it possible to do this with one line of code?
elem.find('input').attr('validate-field','');
elem.find('select').attr('validate-field','');
elem.find('textarea').attr('validate-field','');
$compile(elem.contents())(scope);
}
}
})
.directive('validateField',function(){
return{
restrict: 'A',
require:['^ngModel'],
link: function(scope, element, attr, ctrls){
var valid = false;
var ngModel = ctrls[0];
alert('before validation');
ngModel.$parsers.unshift(function (value){
alert('validating');
valid = validator(value);
ngModel.$setValidity('required', valid, ctrls);
return valid ? value : undefined;
});
}
}
});
上面的代码能够将validate-field
属性添加到表单元素中:
<input type="text" ng-model="model1" validate-field>
问题是ngModel.$parsers.unshift
未被调用,alert('before validation');
被调用,但alert('validating');
未被调用。
我错过了什么?
答案 0 :(得分:2)
目前发生的事情是formValidator
指令链接函数在编译完所有子validateField
指令后进行评估。因为link: function(){ .. }
函数被认为与postLink函数相同,后者在所有子scope
链接就绪后被调用。你的情况也是如此。
所以我说要调用你的链接函数以确保在指令编译之前它会添加validate-fields
。为此,您需要使用preLink
函数。
.directive('formValidator', function($compile) {
return {
restrict: 'A',
link: {
pre: function(scope, elem, attrs) {
elem.find('input').attr('validate-field', '');
elem.find('select').attr('validate-field', '');
elem.find('textarea').attr('validate-field', '');
$compile(elem.contents())(scope);
}
}
}
})
更好的版本只能编译一次父指令
.directive('formValidator', function($compile) {
return {
restrict: 'A',
compile: function(elem, attrs) {
elem.find('input').attr('validate-field', '');
elem.find('select').attr('validate-field', '');
elem.find('textarea').attr('validate-field', '');
//removed to avoid infinite directive compile
elem.removeAttr('form-validator');
var linkFn = $compile(elem);
return function(scope, element, attr) {
linkFn(scope);
}
}
}
})
答案 1 :(得分:1)
也许,您想要使用$formatters
。
plunker上的示例。
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@2.0.0" data-semver="1.4.7" src="https://code.angularjs.org/1.4.7/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular Directive</h1>
<div ng-app="app" ng-controller="ctrl">
<form form-validator="">
<input type="text" ng-model="model1" />
<input type="text" ng-model="model2" />
<input type="text" ng-model="model3" />
<textarea ng-model="model4"></textarea>
<select ng-model="model5">
<option value="1">Name</option>
</select>
<button ng-click="model1='A'"> set model 1 value A</button>
</form>
</div>
<script>
var app=angular.module('app',[])
.controller('ctrl',function($scope){
})
.directive('formValidator',function($compile){
return{
restrict: 'A',
priority:10000,
link: function(scope, elem, attrs){
//is it possible to do this with one line of code?
elem.find('input').attr('validate-field','');
elem.find('select').attr('validate-field','');
elem.find('textarea').attr('validate-field','');
$compile(elem.contents())(scope);
}
}
})
.directive('validateField',function(){
return{
restrict: 'A',
require:'ngModel',
link: function(scope, element, attr, ngModel){
var valid = false;
console.log('before validation',ngModel);
ngModel.$formatters.unshift(function (value){
console.log('validating');
//valid = validator(value);
ngModel.$setValidity('required', valid, ngModel);
return valid ? value : undefined;
});
ngModel.$parsers.unshift(function (value){
console.log('$parsers');
valid = true;
ngModel.$setValidity('required', valid, ngModel);
return valid ? value : undefined;
});
}
}
});
</script>
</body>
</html>
&#13;