※AngularJS 1.4.8 です
AngularJSで、複数チェックボックスの処理するのがどうもよくわからなかったので初心者的メモ。
例えば「どれかチェック必須」とか「2つ以上チェック必須」とかがググってもよくわからなかったので、コントローラを自作してしまった。
例えば Twitter Bootstrap を使ってるとして、1つ以上チェックしてなければ form-group
に has-error
のclassを付けたいという場合。
JavaScript側(コントローラ)
app.controller('checkBoxGroup', ['$scope', '$element', '$parse', function ($scope, $element, $parse) {
// html の方で「self」という文字で状態を検出できるようにする
var self = {
touched: false,
required: (undefined != ($element.attr('required') || $element.attr('ng-required'))),
values: []
};
$scope.self = self;
// 配下にある ng-model を取得
$element.find('[ng-model]').each(function() {
var item = angular.element(this);
if (item.is(':checked') && !item.attr('ng-checked')) {
// checked があればスコープに割当て
$parse(item.attr('ng-model')).assign($scope, true);
// 値を追加
self.values.push(item.val());
}
// クリックされた場合
item.on('click', function() {
// 一度でも触れば touched を true に
if (false == self.touched) {
self.touched = true;
}
if (item.is(':checked')) {
// 値を追加
self.values.push(item.val());
} else {
// 値を削除
var i = self.values.indexOf(item.val());
if (i >= 0) {
self.values.splice(i, 1);
}
}
});
});
}]);
HTML側
<div class="form-group" ng-controller="checkBoxGroup" ng-class="{'has-error': self.required && self.touched && self.values.length < 1}" required>
<label class="control-label">どれかチェックしてね</label>
<div class="checkbox">
<label><input type="checkbox" name="chk[]" value="項目1" ng-model="chk1" ng-required="self.required && self.values.length < 1">項目1</label>
<label><input type="checkbox" name="chk[]" value="項目2" ng-model="chk2" ng-required="self.required && self.values.length < 1">項目2</label>
<label><input type="checkbox" name="chk[]" value="項目3" ng-model="chk3" ng-required="self.required && self.values.length < 1" checked>項目3</label>
<label><input type="checkbox" name="chk[]" value="項目4" ng-model="chk4" ng-required="self.required && self.values.length < 1">項目4</label>
</div>
</div>
- JavaScript側では
ng-controller
が指定された要素内にあるチェックボックスを検出して、HTML側でチェック状態が読めるように処理をしてる。 - HTML側ではJavaScriptで処理されたチェックボックスの状態(この場合
self
という変数)を使って監視してる。-
self.touched
で「1度でもクリック(チェック)されれば」という処理を監視して、初期状態では何もしないようにしてる。 -
required
またはng-required
があれば配下のチェックボックスを必須項目とする。
-
-
self.values.length < 1
により「1つ以上チェックされていなければ」という条件になる。
2つ以上ならself.values.length < 2
。 - チェックボックスの
ng-model
の名前はユニークにする必要がある。 - ついでに、チェックボックスに
checked
があればデフォルトでチェックを入れる。- AngularJS管理下では
checked
ではなくng-init="ngModel名=true"
みたいにしないとデフォルトで checked にならないので。
- AngularJS管理下では
追記
「AngularJS管理下では checked
が効かない」という点について、input type="text"
とかの value
とか select
の selected
とかも効かないようなので以下のようなスニペットも作ってみた。
手探りなのでこれも正解かどうかはわからない。
// さいしょ
var app = angular.module('app', []);
// 直後にこれ実行しておく
app.run(['$rootScope', '$rootElement', '$parse', function($rootScope, $rootElement, $parse) {
$rootElement.find('[ng-model]').each(function(i, e) {
var item = angular.element(this);
var type = item.attr('type')||this.tagName.toLowerCase();
if (/^(checkbox|radio)$/.test(type)) {
if (item.is(':checked') && !item.attr('ng-checked')) {
$parse(item.attr('ng-model')).assign($rootScope, true);
}
} else if (item.val()) {
$parse(item.attr('ng-model')).assign($rootScope, item.val());
}
});
}]);
普通に一般的な機能なので、たぶん車輪の再開発してるんじゃないかと思うけど・・・w