次の(非常に単純な)データ構造があるとしましょう。
$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-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
フィールド2を39まで下げると、フィールド2は有効になりますが、フィールド1はまだ無効です。アイデア?
OK、次の作品(再び、「共有」は「パーセント」です):
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」として設定し、値を監視するオブジェクトのセットに設定します。
Angleuiライブラリを確認できます( i-utility part)。 ui-validateディレクティブがあります。
それを実装する1つの方法は
<input type="number" name="accountNo" ng-model="account.percent"
ui-validate="{overflow : 'checkOverflow($value,account)' }">
コントローラーで、アカウントの計算に基づいてtrueまたはfalseを返すメソッドcheckOverflow
を作成します。
私はこれを自分で試したことはありませんが、アイデアを共有したいと思います。サイトにあるサンプルも読んでください。
フォームに入力フィールドの可変数を持つことができる動的フォームがあり、追加する入力コントロールの数を制限する必要がある場合があります。
これらの入力フィールドは他の要因の組み合わせによって生成されるため、これらの入力フィールドの追加を簡単に制限できませんでした。したがって、入力フィールドの数が制限を超えた場合、フォームを無効にする必要がありました。コントローラー_ctrl.myForm
_でフォームへの参照を作成してこれを行い、入力コントローラーが(コントローラーコードで)動的に生成されるたびに、制限チェックを行い、フォームに有効性を設定しますこのような:ctrl.myForm.$setValidity("maxCount", false);
検証は特定の入力フィールドではなく、入力の総数によって決定されるため、これはうまく機能しました。複数のフィールドの組み合わせによって決定される検証を行う必要がある場合、この同じアプローチが機能します。
私の正気のために
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
}
});
このチェックのみを行う単一のディレクティブを定義できます。
<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);
}
};
}
};
});
ここ はプランカーです。