假设我有以下(非常简单的)数据结构:
$scope.accounts = [{
percent: 30,
name: "Checking"},
{ percent: 70,
name: "Savings"}];
然后我将以下结构作为表单的一部分:
<div ng-repeat="account in accounts">
<input type="number" max="100" min="0" ng-model="account.percent" />
<input type="text" ng-model="account.name" />
</div>
现在,我想验证每组帐户的百分比总和为100,但我看到的自定义指令的大多数示例仅涉及验证单个值。创建一个可以一次验证多个依赖字段的指令的惯用方法是什么?在jquery中有相当多的解决方案,但我还没有找到Angular的良好来源。
编辑:我想出了以下自定义指令(“share”是原始代码的“百分比”的同义词)。 share-validate指令采用"{group: accounts, id: $index}"
形式的映射作为其值。
app.directive('shareValidate', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
params = angular.copy(scope.$eval(attr.shareValidate));
params.group.splice(params.id, 1);
var sum = +viewValue;
angular.forEach(params.group, function(entity, index) {
sum += +(entity.share);
});
ctrl.$setValidity('share', sum === 100);
return viewValue;
});
}
};
});
此ALMOST有效,但无法处理字段无效的情况,但另一个字段的后续更改会使其再次有效。例如:
Field 1: 61
Field 2: 52
如果我将Field 2降低到39,则Field 2现在有效,但Field 1仍然无效。想法?
答案 0 :(得分:14)
好的,以下作品(再次,“分享”是“百分比”):
app.directive('shareValidate', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.$watch(attr.shareValidate, function(newArr, oldArr) {
var sum = 0;
angular.forEach(newArr, function(entity, i) {
sum += entity.share;
});
if (sum === 100) {
ctrl.$setValidity('share', true);
scope.path.offers.invalidShares = false;
}
else {
ctrl.$setValidity('share', false);
scope.path.offers.invalidShares = true;
}
}, true); //enable deep dirty checking
}
};
});
在HTML中,将属性设置为“share-validate”,并将值设置为您要观看的对象集。
答案 1 :(得分:8)
您可以查看angularui库(ui-utility部分)。它有ui-validate指令。
您可以实现它的一种方法是
<input type="number" name="accountNo" ng-model="account.percent"
ui-validate="{overflow : 'checkOverflow($value,account)' }">
在控制器上创建方法checkOverflow
,该方法根据帐户计算返回true或false。
我自己没有尝试过,但想分享这个想法。阅读网站上的样本。
答案 2 :(得分:5)
我有一个案例,我有一个动态表单,我可以在我的表单上有可变数量的输入字段,我需要限制正在添加的输入控件的数量。
我不能轻易地限制这些输入字段的添加,因为它们是由其他因素的组合生成的,所以如果输入字段的数量超出限制,我需要使表单无效。我这样做是通过在我的控制器ctrl.myForm
中创建对表单的引用,然后每次动态生成输入控件(在我的控制器代码中),我会进行限制检查,然后在表单上设置有效性像这样:ctrl.myForm.$setValidity("maxCount", false);
这很有效,因为验证不是由特定的输入字段决定的,而是我输入的总数。如果您需要通过多个字段的组合确定需要进行的验证,则可以使用相同的方法。
答案 3 :(得分:0)
为了我的理智
HTML
<form ng-submit="applyDefaultDays()" name="daysForm" ng-controller="DaysCtrl">
<div class="form-group">
<label for="startDate">Start Date</label>
<div class="input-group">
<input id="startDate"
ng-change="runAllValidators()"
ng-model="startDate"
type="text"
class="form-control"
name="startDate"
placeholder="mm/dd/yyyy"
ng-required
/>
</div>
</div>
<div class="form-group">
<label for="eEndDate">End Date</label>
<div class="input-group">
<input id="endDate"
ng-change="runAllValidators()"
ng-model="endDate"
type="text"
class="form-control"
name="endDate"
placeholder="mm/dd/yyyy"
ng-required
/>
</div>
</div>
<div class="text-right">
<button ng-disabled="daysForm.$invalid" type="submit" class="btn btn-default">Apply Default Dates</button>
</div>
JS
'use strict';
angular.module('myModule')
.controller('DaysCtrl', function($scope, $timeout) {
$scope.initDate = new Date();
$scope.startDate = angular.copy($scope.initDate);
$scope.endDate = angular.copy($scope.startDate);
$scope.endDate.setTime($scope.endDate.getTime() + 6*24*60*60*1000);
$scope.$watch("daysForm", function(){
//fields are only populated after controller is initialized
$timeout(function(){
//not all viewalues are set yet for somereason, timeout needed
$scope.daysForm.startDate.$validators.checkAgainst = function(){
$scope.daysForm.startDate.$setDirty();
return (new Date($scope.daysForm.startDate.$viewValue)).getTime() <=
(new Date($scope.daysForm.endDate.$viewValue)).getTime();
};
$scope.daysForm.endDate.$validators.checkAgainst = function(){
$scope.daysForm.endDate.$setDirty();
return (new Date($scope.daysForm.startDate.$viewValue)).getTime() <=
(new Date($scope.daysForm.endDate.$viewValue)).getTime();
};
});
});
$scope.runAllValidators = function(){
//need to run all validators on change
$scope.daysForm.startDate.$validate();
$scope.daysForm.endDate.$validate();
};
$scope.applyDefaultDays = function(){
//do stuff
}
});
答案 4 :(得分:0)
您可以定义仅负责此检查的单个指令。
<form>
<div ng-repeat="account in accounts">
<input type="number" max="100" min="0" ng-model="account.percent" />
<input type="text" ng-model="account.name" />
</div>
<!-- HERE IT IS -->
<sum-up-to-hundred accounts="accounts"></sum-up-to-hundred>
</form>
这是简单指令的代码。
app.directive('sumUpToHundred', function() {
return {
scope: {
accounts: '<'
},
require: {
formCtrl: '^form'
},
bindToController: true,
controllerAs: '$ctrl',
controller: function() {
var vm = this;
vm.$doCheck = function(changes) {
var sum = vm.accounts.map((a)=> a.percent).reduce((total, n)=> total + n);
if (sum !== 100) {
vm.formCtrl.$setValidity('sumuptohundred', false);
} else {
vm.formCtrl.$setValidity('sumuptohundred', true);
}
};
}
};
});
Here是一名掠夺者。