I am looking for something exactly like these (tri-state checkboxes with "parents"). But using that solution wouldn't be elegant, as I do not depend on jQuery right now, and I would need to call $scope.$apply to get the model to recognize the automatically (un)checked checkboxed jQuery clicked.
Here's a bug for angular.js that requests ng-indeterminate-value implemented. But that still wouldn't give me the synchronization to all the children, which is something I don't think should be a part of my controller.
What I am looking for would be something like this:
<input type="checkbox" ng-children-model="child.isSelected for child in listelements">
. The list of booleans would be computed, and if 0 selected -> checkbox false. If all selected -> checkbox true. Else -> checkbox indeterminate.$scope.listelements = [{isSelected: true, desc: "Donkey"},{isSelected: false, desc: "Horse"}]
<tr ng-repeat="elem in listelements"><td><input type="checkbox" ng-model="elem.isSelected"></td><td>{{elem.desc}}</td></tr>
.Code Explanation For the input types as button, we have created one button for selecting the checkboxes where onClick (), the selects () function will be invoked and the other one for deselecting the checkboxes (if selected any/all) where onClick () the deselect () function will be invoked.
Checkboxes actually has three states: true, false and indeterminate which indicates that a checkbox is neither "on" or "off". A checkbox cannot be set to indeterminate state by an HTML attribute - it must be set by a JavaScript. This state can be used to force the user to check or uncheck the checkbox.
Note: If you submit a form with an indeterminate checkbox, the same thing happens as if the checkbox were unchecked — no data is submitted to represent the checkbox.
Since you want a new type/kind of component, this sounds like a good case for a custom directive.
Since the parent/master/tri-stated checkbox and the individual dual-state checkboxes need to interact with each other, I suggest a single directive, with its own controller, to handle the logic.
<tri-state-checkbox checkboxes="listelements"></tri-state-checkbox>
Directive:
app.directive('triStateCheckbox', function() { return { replace: true, restrict: 'E', scope: { checkboxes: '=' }, template: '<div><input type="checkbox" ng-model="master" ng-change="masterChange()">' + '<div ng-repeat="cb in checkboxes">' + '<input type="checkbox" ng-model="cb.isSelected" ng-change="cbChange()">{{cb.desc}}' + '</div>' + '</div>', controller: function($scope, $element) { $scope.masterChange = function() { if($scope.master) { angular.forEach($scope.checkboxes, function(cb, index){ cb.isSelected = true; }); } else { angular.forEach($scope.checkboxes, function(cb, index){ cb.isSelected = false; }); } }; var masterCb = $element.children()[0]; $scope.cbChange = function() { var allSet = true, allClear = true; angular.forEach($scope.checkboxes, function(cb, index){ if(cb.isSelected) { allClear = false; } else { allSet = false; } }); if(allSet) { $scope.master = true; masterCb.indeterminate = false; } else if(allClear) { $scope.master = false; masterCb.indeterminate = false; } else { $scope.master = false; masterCb.indeterminate = true; } }; $scope.cbChange(); // initialize }, }; });
Change the template to suit your needs, or use an external template with templateUrl.
The directive assumes that the checkboxes array contains objects that have an isSelected
property and a desc
property.
Plunker.
Update: If you prefer to have the directive only render the tri-stated checkbox, hence the individual checkboxes are in the HTML (like @Piran's solution), here's another plunker variation for that. For this plunker, the HTML would be:
<tri-state-checkbox checkboxes="listelements" class="select-all-cb"> </tri-state-checkbox>select all <div ng-repeat="item in listelements"> <input type="checkbox" ng-model="item.isSelected"> {{item.desc}} </div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With