ChatGPT解决这个技术问题 Extra ChatGPT

How do I bind to list of checkbox values with AngularJS?

I have a few checkboxes:

<input type='checkbox' value="apple" checked>
<input type='checkbox' value="orange">
<input type='checkbox' value="pear" checked>
<input type='checkbox' value="naartjie">

That I would like to bind to a list in my controller such that whenever a checkbox is changed the controller maintains a list of all the checked values, for example, ['apple', 'pear'].

ng-model seems to only be able to bind the value of one single checkbox to a variable in the controller.

Is there another way to do it so that I can bind the four checkboxes to a list in the controller?

Does it have to be a list? Would an object work?: <input type='checkbox' ng-model="checkboxes.apple">, etc. Model would be:{"apple":true,"orange":false,"pear":true,"naartjie":true}
Try the directive in this Repo
Be sure to look past the accepted answer. There's another answer which is, in my opinion, much more elegant.
naartjie !? That just gives you away boet! :D
@ppumkin hehe just seen this. You're right :D

P
Peter Mortensen

There are two ways to approach this problem. Either use a simple array or an array of objects. Each solution has it pros and cons. Below you'll find one for each case.

With a simple array as input data

The HTML could look like:

<label ng-repeat="fruitName in fruits">
  <input
    type="checkbox"
    name="selectedFruits[]"
    value="{{fruitName}}"
    ng-checked="selection.indexOf(fruitName) > -1"
    ng-click="toggleSelection(fruitName)"
  > {{fruitName}}
</label>

And the appropriate controller code would be:

app.controller('SimpleArrayCtrl', ['$scope', function SimpleArrayCtrl($scope) {

  // Fruits
  $scope.fruits = ['apple', 'orange', 'pear', 'naartjie'];

  // Selected fruits
  $scope.selection = ['apple', 'pear'];

  // Toggle selection for a given fruit by name
  $scope.toggleSelection = function toggleSelection(fruitName) {
    var idx = $scope.selection.indexOf(fruitName);

    // Is currently selected
    if (idx > -1) {
      $scope.selection.splice(idx, 1);
    }

    // Is newly selected
    else {
      $scope.selection.push(fruitName);
    }
  };
}]);

Pros: Simple data structure and toggling by name is easy to handle

Cons: Add/remove is cumbersome as two lists (the input and selection) have to be managed

With an object array as input data

The HTML could look like:

<label ng-repeat="fruit in fruits">
  <!--
    - Use `value="{{fruit.name}}"` to give the input a real value, in case the form gets submitted
      traditionally

    - Use `ng-checked="fruit.selected"` to have the checkbox checked based on some angular expression
      (no two-way-data-binding)

    - Use `ng-model="fruit.selected"` to utilize two-way-data-binding. Note that `.selected`
      is arbitrary. The property name could be anything and will be created on the object if not present.
  -->
  <input
    type="checkbox"
    name="selectedFruits[]"
    value="{{fruit.name}}"
    ng-model="fruit.selected"
  > {{fruit.name}}
</label>

And the appropriate controller code would be:

app.controller('ObjectArrayCtrl', ['$scope', 'filterFilter', function ObjectArrayCtrl($scope, filterFilter) {

  // Fruits
  $scope.fruits = [
    { name: 'apple',    selected: true },
    { name: 'orange',   selected: false },
    { name: 'pear',     selected: true },
    { name: 'naartjie', selected: false }
  ];

  // Selected fruits
  $scope.selection = [];

  // Helper method to get selected fruits
  $scope.selectedFruits = function selectedFruits() {
    return filterFilter($scope.fruits, { selected: true });
  };

  // Watch fruits for changes
  $scope.$watch('fruits|filter:{selected:true}', function (nv) {
    $scope.selection = nv.map(function (fruit) {
      return fruit.name;
    });
  }, true);
}]);

Pros: Add/remove is very easy

Cons: Somewhat more complex data structure and toggling by name is cumbersome or requires a helper method

Demo: http://jsbin.com/ImAqUC/1/


FYI, instead of injecting $filter, you can inject filterFilter, and then use as follows: return filterFilter($scope.fruits, {checked: true}); Built-in and custom filters are registered with the $injector with name filterNameFilter ("filterName" should be in italics) -- $filterProvider docs
value="{{fruit.name}}" and ng-checked="fruit.checked" are superfluous, since ng-model is used.
I noticed that there is no need to specify "checked" in the model, Angular will set the property automatically :)
Should use ng-change instead of ng-click because it deals with edge cases better.
@ViktorMolokostov That would just be useful, if you were to submit the form traditionally. Meaning posting it to the action handler (some server side script). With php, a form element with a name like that (using the square brackets) creates an array in the request data. This way you could easily handle the selected fruits.
e
ephemerr

A simple solution:

<div ng-controller="MainCtrl">
  <label ng-repeat="(color,enabled) in colors">
      <input type="checkbox" ng-model="colors[color]" /> {{color}} 
  </label>
  <p>colors: {{colors}}</p>
</div>

<script>
  var app = angular.module('plunker', []);

  app.controller('MainCtrl', function($scope){
      $scope.colors = {Blue: true, Orange: true};
  });
</script>

http://plnkr.co/edit/U4VD61?p=preview


@kolypto - this is definitely the answer. I rewrote it for people (like me) who are working with objects: plnkr.co/edit/cqsADe8lKegsBMgWMyB8?p=preview
I do it just as you do, but what does the enabled in (color,enabled) in colors do?
@Sebastian, since colors is an object, when you iterate it -- you get pairs of (key,value).
Though I like this answer very much! I think, there is one major problem with using objects as data source. That is, because by definition the order of objects properties is undefined, one can't provide a definite order when displaying the checkboxes. Still +1 ;)
colors should be named isSelected, it is much easier to read isSelected[color] than colors[color]
R
Rubens Mariuzzo
<input type='checkbox' ng-repeat="fruit in fruits"
  ng-checked="checkedFruits.indexOf(fruit) != -1" ng-click="toggleCheck(fruit)">

.

function SomeCtrl ($scope) {
    $scope.fruits = ["apple, orange, pear, naartjie"];
    $scope.checkedFruits = [];
    $scope.toggleCheck = function (fruit) {
        if ($scope.checkedFruits.indexOf(fruit) === -1) {
            $scope.checkedFruits.push(fruit);
        } else {
            $scope.checkedFruits.splice($scope.checkedFruits.indexOf(fruit), 1);
        }
    };
}

Love how simple this is, exactly what I am looking for (although I have to admit that @vitalets directive is amazing). I have modified Umur's code a little to create this fiddle: jsfiddle.net/samurai_jane/9mwsbfuc
I make the words of the Samurai Jane mine! How simple it was to show just what I needed! :)
M
Michelle Tilley

Here's a quick little reusable directive that seems to do what you're looking to do. I've simply called it checkList. It updates the array when the checkboxes change, and updates the checkboxes when the array changes.

app.directive('checkList', function() {
  return {
    scope: {
      list: '=checkList',
      value: '@'
    },
    link: function(scope, elem, attrs) {
      var handler = function(setup) {
        var checked = elem.prop('checked');
        var index = scope.list.indexOf(scope.value);

        if (checked && index == -1) {
          if (setup) elem.prop('checked', false);
          else scope.list.push(scope.value);
        } else if (!checked && index != -1) {
          if (setup) elem.prop('checked', true);
          else scope.list.splice(index, 1);
        }
      };

      var setupHandler = handler.bind(null, true);
      var changeHandler = handler.bind(null, false);

      elem.bind('change', function() {
        scope.$apply(changeHandler);
      });
      scope.$watch('list', setupHandler, true);
    }
  };
});

Here's a controller and a view that shows how you might go about using it.

<div ng-app="myApp" ng-controller='MainController'>
  <span ng-repeat="fruit in fruits">
    <input type='checkbox' value="{{fruit}}" check-list='checked_fruits'> {{fruit}}<br />
  </span>

  <div>The following fruits are checked: {{checked_fruits | json}}</div>

  <div>Add fruit to the array manually:
    <button ng-repeat="fruit in fruits" ng-click='addFruit(fruit)'>{{fruit}}</button>
  </div>
</div>
app.controller('MainController', function($scope) {
  $scope.fruits = ['apple', 'orange', 'pear', 'naartjie'];
  $scope.checked_fruits = ['apple', 'pear'];
  $scope.addFruit = function(fruit) {
    if ($scope.checked_fruits.indexOf(fruit) != -1) return;
    $scope.checked_fruits.push(fruit);
  };
});

(The buttons demonstrate that changing the array will also update the checkboxes.)

Finally, here is an example of the directive in action on Plunker: http://plnkr.co/edit/3YNLsyoG4PIBW6Kj7dRK?p=preview


Thanks Brandon, this did exactly what I wanted (and exactly what the question asked for too, unlike the other answers). The only tweak I made was to change your "elem.on('change', function() ..." to "elem.bind('change', function()..." so as to remove the dependency on jQuery.
This is pretty neat, but somehow destroys my ability to use ng-disabled :( Is there any way I can fix that?
Super useful! And even worked for me with objects instead of arrays for both the source list and the data list!
I agree with everyone. This one is the most useful and undoubtedly reusable one!! Thanks for the good work. :)
If you are having problems with AngularJS >= 1.4.4, check github.com/angular/angular.js/issues/13037: replace value: '@' by value: '=ngValue'
v
vitalets

Based on answers in this thread I've created checklist-model directive that covers all cases:

simple array of primitives

array of objects (pick id or whole object)

object properties iteration

For topic-starter case it would be:

<label ng-repeat="fruit in ['apple', 'orange', 'pear', 'naartjie']">
    <input type="checkbox" checklist-model="selectedFruits" checklist-value="fruit"> {{fruit}}
</label>

That looks like what I need. Is there any chance you could explain how to use it when getting the data asynchronously? That part is confusing to me.
After you're getting the data asynchronously just modify the checlist model in the scope, in the above example selectedFruits.
P
Peter Mortensen

Using a string of $index can help to use a hashmap of selected values:

<ul>
    <li ng-repeat="someItem in someArray">
        <input type="checkbox" ng-model="someObject[$index.toString()]" />
    </li>
</ul>

This way the ng-model object gets updated with the key representing the index.

$scope.someObject = {};

After a while $scope.someObject should look something like:

$scope.someObject = {
     0: true,
     4: false,
     1: true
};

This method won't work for all situations, but it is easy to implement.


This is a very elegant solution and fits my case (using AJAX)
uses the kiss method
M
Mark Rajcok

Since you accepted an answer in which a list was not used, I'll assume the answer to my comment question is "No, it doesn't have to be a list". I also had the impression that maybe you were rending the HTML server side, since "checked" is present in your sample HTML (this would not be needed if ng-model were used to model your checkboxes).

Anyway, here's what I had in mind when I asked the question, also assuming you were generating the HTML server-side:

<div ng-controller="MyCtrl" 
 ng-init="checkboxes = {apple: true, orange: false, pear: true, naartjie: false}">
    <input type="checkbox" ng-model="checkboxes.apple">apple
    <input type="checkbox" ng-model="checkboxes.orange">orange
    <input type="checkbox" ng-model="checkboxes.pear">pear
    <input type="checkbox" ng-model="checkboxes.naartjie">naartjie
    <br>{{checkboxes}}
</div>

ng-init allows server-side generated HTML to initially set certain checkboxes.

Fiddle.


P
Peter Mortensen

I think the easiest workaround would be to use 'select' with 'multiple' specified:

<select ng-model="selectedfruit" multiple ng-options="v for v in fruit"></select>

Otherwise, I think you'll have to process the list to construct the list (by $watch()ing the model array bind with checkboxes).


He's asking for a checkbox list, and yet you are telling him about select with options. Which is completely different.
@CrazySabbath: yet you are not understanding that he suggesting an alternate solution and this answer helped 6 other people as an "alternate solution"
M
Mohammed Safeer

The following solution seems like a good option,

<label ng-repeat="fruit in fruits">
  <input
    type="checkbox"
    ng-model="fruit.checked"
    ng-value="true"
  > {{fruit.fruitName}}
</label>

And in controller model value fruits will be like this

$scope.fruits = [
  {
    "name": "apple",
    "checked": true
  },
  {
    "name": "orange"
  },
  {
    "name": "grapes",
    "checked": true
  }
];

the more I am looking at these examples it seems i will have to map my array into an array of objects.
A
Adam

I have adapted Yoshi's accepted answer to deal with complex objects (instead of strings).

HTML

<div ng-controller="TestController">
    <p ng-repeat="permission in allPermissions">
        <input type="checkbox" ng-checked="selectedPermissions.containsObjectWithProperty('id', permission.id)" ng-click="toggleSelection(permission)" />
        {{permission.name}}
    </p>

    <hr />

    <p>allPermissions: | <span ng-repeat="permission in allPermissions">{{permission.name}} | </span></p>
    <p>selectedPermissions: | <span ng-repeat="permission in selectedPermissions">{{permission.name}} | </span></p>
</div>

JavaScript

Array.prototype.indexOfObjectWithProperty = function(propertyName, propertyValue)
{
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][propertyName] === propertyValue) return i;
    }

    return -1;
};


Array.prototype.containsObjectWithProperty = function(propertyName, propertyValue)
{
    return this.indexOfObjectWithProperty(propertyName, propertyValue) != -1;
};


function TestController($scope)
{
    $scope.allPermissions = [
    { "id" : 1, "name" : "ROLE_USER" },
    { "id" : 2, "name" : "ROLE_ADMIN" },
    { "id" : 3, "name" : "ROLE_READ" },
    { "id" : 4, "name" : "ROLE_WRITE" } ];

    $scope.selectedPermissions = [
    { "id" : 1, "name" : "ROLE_USER" },
    { "id" : 3, "name" : "ROLE_READ" } ];

    $scope.toggleSelection = function toggleSelection(permission) {
        var index = $scope.selectedPermissions.indexOfObjectWithProperty('id', permission.id);

        if (index > -1) {
            $scope.selectedPermissions.splice(index, 1);
        } else {
            $scope.selectedPermissions.push(permission);
        }
    };
}

Working example: http://jsfiddle.net/tCU8v/


You should never have an <input type="checkbox"> without a wrapping or matching <label>! Now your users have to click on the actual checkbox instead of the text next to the checkbox, which is much harder and is bad usability.
A
Adrian Stanescu

Another simple directive could be like:

var appModule = angular.module("appModule", []);

appModule.directive("checkList", [function () {
return {
    restrict: "A",
    scope: {
        selectedItemsArray: "=",
        value: "@"
    },
    link: function (scope, elem) {
        scope.$watchCollection("selectedItemsArray", function (newValue) {
            if (_.contains(newValue, scope.value)) {
                elem.prop("checked", true);
            } else {
                elem.prop("checked", false);
            }
        });
        if (_.contains(scope.selectedItemsArray, scope.value)) {
            elem.prop("checked", true);
        }
        elem.on("change", function () {
            if (elem.prop("checked")) {
                if (!_.contains(scope.selectedItemsArray, scope.value)) {
                    scope.$apply(
                        function () {
                            scope.selectedItemsArray.push(scope.value);
                        }
                    );
                }
            } else {
                if (_.contains(scope.selectedItemsArray, scope.value)) {
                    var index = scope.selectedItemsArray.indexOf(scope.value);
                    scope.$apply(
                        function () {
                            scope.selectedItemsArray.splice(index, 1);
                        });
                }
            }
            console.log(scope.selectedItemsArray);
        });
    }
};
}]);

The controller:

appModule.controller("sampleController", ["$scope",
  function ($scope) {
    //#region "Scope Members"
    $scope.sourceArray = [{ id: 1, text: "val1" }, { id: 2, text: "val2" }];
    $scope.selectedItems = ["1"];
    //#endregion
    $scope.selectAll = function () {
      $scope.selectedItems = ["1", "2"];
  };
    $scope.unCheckAll = function () {
      $scope.selectedItems = [];
    };
}]);

And the HTML:

<ul class="list-unstyled filter-list">
<li data-ng-repeat="item in sourceArray">
    <div class="checkbox">
        <label>
            <input type="checkbox" check-list selected-items-array="selectedItems" value="{{item.id}}">
            {{item.text}}
        </label>
    </div>
</li>

I'm also including a Plunker: http://plnkr.co/edit/XnFtyij4ed6RyFwnFN6V?p=preview


P
Paul B. Hartzog

You don't have to write all that code. AngularJS will keep the model and the checkboxes in sync simply by using ngTrueValue and ngFalseValue

Codepen here: http://codepen.io/paulbhartzog/pen/kBhzn

Code snippet:

<p ng-repeat="item in list1" class="item" id="{{item.id}}">
  <strong>{{item.id}}</strong> <input name='obj1_data' type="checkbox" ng-model="list1[$index].data" ng-true-value="1" ng-false-value="0"> Click this to change data value below
</p>
<pre>{{list1 | json}}</pre>

This is not what OP’s asking for.
Binding checkboxes to a list is what was asked, and what I did. The array can be modified to suit the application. The point is that the checkboxes are bound. ngTrueValue and ngFalseValue can also be used to map to a second array that only lists other attributes, such as names.
OP wants a list of checked values, not a list of all values, checked and unchecked.
D
DSB

Check out this directive that manages effectively lists of checkboxes. I hope it works for you. CheckList Model


P
Peter Mortensen

There is a way to work on the array directly and use ng-model at the same time through ng-model-options="{ getterSetter: true }".

The trick is to use a getter/setter function in your ng-model. This way you can use an array as your real model and "fake" the booleans in the input's model:

<label ng-repeat="fruitName in ['apple', 'orange', 'pear', 'naartjie']">
  <input
    type="checkbox"
    ng-model="fruitsGetterSetterGenerator(fruitName)"
    ng-model-options="{ getterSetter: true }"
  > {{fruitName}}
</label>

$scope.fruits = ['apple', 'pear']; // pre checked

$scope.fruitsGetterSetterGenerator = function(fruitName){
    return function myGetterSetter(nowHasFruit){
        if (nowHasFruit !== undefined){

            // Setter
            fruitIndex = $scope.fruits.indexOf(fruit);
            didHaveFruit = (fruitIndex !== -1);
            mustAdd = (!didHaveFruit && nowHasFruit);
            mustDel = (didHaveFruit && !nowHasFruit);
            if (mustAdd){
                $scope.fruits.push(fruit);
            }
            if (mustDel){
                $scope.fruits.splice(fruitIndex, 1);
            }
        }
        else {
            // Getter
            return $scope.user.fruits.indexOf(fruit) !== -1;
        }
    }
}

CAVEAT You shouldn't use this method if your arrays are big as myGetterSetter will be called a lot of times.

For more on that, see https://docs.angularjs.org/api/ng/directive/ngModelOptions.


B
Bikey

I like Yoshi's answer. I enhanced it so You can use the same function for multiple lists.

<label ng-repeat="fruitName in fruits">
<input
type="checkbox"
name="selectedFruits[]"
value="{{fruitName}}"
ng-checked="selection.indexOf(fruitName) > -1"
ng-click="toggleSelection(fruitName, selection)"> {{fruitName}}
</label>


<label ng-repeat="veggieName in veggies">
<input
type="checkbox"
name="selectedVeggies[]"
value="{{veggieName}}"
ng-checked="veggieSelection.indexOf(veggieName) > -1"
ng-click="toggleSelection(veggieName, veggieSelection)"> {{veggieName}}
</label>



app.controller('SimpleArrayCtrl', ['$scope', function SimpleArrayCtrl($scope) {
  // fruits
  $scope.fruits = ['apple', 'orange', 'pear', 'naartjie'];
  $scope.veggies = ['lettuce', 'cabbage', 'tomato']
  // selected fruits
  $scope.selection = ['apple', 'pear'];
  $scope.veggieSelection = ['lettuce']
  // toggle selection for a given fruit by name
  $scope.toggleSelection = function toggleSelection(selectionName, listSelection) {
    var idx = listSelection.indexOf(selectionName);

    // is currently selected
    if (idx > -1) {
      listSelection.splice(idx, 1);
    }

    // is newly selected
    else {
      listSelection.push(selectionName);
    }
  };
}]);

http://plnkr.co/edit/KcbtzEyNMA8s1X7Hja8p?p=preview


s
sudo bangbang

If you have multiple checkboxes on the same form

The controller code

vm.doYouHaveCheckBox = ['aaa', 'ccc', 'bbb'];
vm.desiredRoutesCheckBox = ['ddd', 'ccc', 'Default'];
vm.doYouHaveCBSelection = [];
vm.desiredRoutesCBSelection = [];

View code

<div ng-repeat="doYouHaveOption in vm.doYouHaveCheckBox">
    <div class="action-checkbox">
        <input id="{{doYouHaveOption}}" type="checkbox" value="{{doYouHaveOption}}" ng-checked="vm.doYouHaveCBSelection.indexOf(doYouHaveOption) > -1" ng-click="vm.toggleSelection(doYouHaveOption,vm.doYouHaveCBSelection)" />
        <label for="{{doYouHaveOption}}"></label>
        {{doYouHaveOption}}
    </div>
</div>

<div ng-repeat="desiredRoutesOption in vm.desiredRoutesCheckBox">
     <div class="action-checkbox">
          <input id="{{desiredRoutesOption}}" type="checkbox" value="{{desiredRoutesOption}}" ng-checked="vm.desiredRoutesCBSelection.indexOf(desiredRoutesOption) > -1" ng-click="vm.toggleSelection(desiredRoutesOption,vm.desiredRoutesCBSelection)" />
          <label for="{{desiredRoutesOption}}"></label>
          {{desiredRoutesOption}}
     </div>
</div>        

V
Vikas Gautam

Inspired from Yoshi's post above. Here is the plnkr.

(function () { angular .module("APP", []) .controller("demoCtrl", ["$scope", function ($scope) { var dc = this dc.list = [ "Selection1", "Selection2", "Selection3" ] dc.multipleSelections = [] dc.individualSelections = [] // Using splice and push methods to make use of // the same "selections" object passed by reference to the // addOrRemove function as using "selections = []" // creates a new object within the scope of the // function which doesn't help in two way binding. dc.addOrRemove = function (selectedItems, item, isMultiple) { var itemIndex = selectedItems.indexOf(item) var isPresent = (itemIndex > -1) if (isMultiple) { if (isPresent) { selectedItems.splice(itemIndex, 1) } else { selectedItems.push(item) } } else { if (isPresent) { selectedItems.splice(0, 1) } else { selectedItems.splice(0, 1, item) } } } }]) })() label { display: block; }

checkbox-select demo

Multiple Selections

dc.multipleSelections :- {{dc.multipleSelections}}


Individual Selections

dc.invidualSelections :- {{dc.individualSelections}}


C
Community

Based on my other post here, I have made a reusable directive.

Check out the GitHub repository

(function () { angular .module("checkbox-select", []) .directive("checkboxModel", ["$compile", function ($compile) { return { restrict: "A", link: function (scope, ele, attrs) { // Defining updateSelection function on the parent scope if (!scope.$parent.updateSelections) { // Using splice and push methods to make use of // the same "selections" object passed by reference to the // addOrRemove function as using "selections = []" // creates a new object within the scope of the // function which doesn't help in two way binding. scope.$parent.updateSelections = function (selectedItems, item, isMultiple) { var itemIndex = selectedItems.indexOf(item) var isPresent = (itemIndex > -1) if (isMultiple) { if (isPresent) { selectedItems.splice(itemIndex, 1) } else { selectedItems.push(item) } } else { if (isPresent) { selectedItems.splice(0, 1) } else { selectedItems.splice(0, 1, item) } } } } // Adding or removing attributes ele.attr("ng-checked", attrs.checkboxModel + ".indexOf(" + attrs.checkboxValue + ") > -1") var multiple = attrs.multiple ? "true" : "false" ele.attr("ng-click", "updateSelections(" + [attrs.checkboxModel, attrs.checkboxValue, multiple].join(",") + ")") // Removing the checkbox-model attribute, // it will avoid recompiling the element infinitly ele.removeAttr("checkbox-model") ele.removeAttr("checkbox-value") ele.removeAttr("multiple") $compile(ele)(scope) } } }]) // Defining app and controller angular .module("APP", ["checkbox-select"]) .controller("demoCtrl", ["$scope", function ($scope) { var dc = this dc.list = [ "selection1", "selection2", "selection3" ] // Define the selections containers here dc.multipleSelections = [] dc.individualSelections = [] }]) })() label { display: block; }

checkbox-select demo

Multiple Selections

dc.multipleSelecitons:- {{dc.multipleSelections}}

Individual Selections

dc.individualSelecitons:- {{dc.individualSelections}}


P
Peter Mortensen

In the HTML (supposing that the checkboxes are in the first column of every row in a table).

<tr ng-repeat="item in fruits">
    <td><input type="checkbox" ng-model="item.checked" ng-click="getChecked(item)"></td>
    <td ng-bind="fruit.name"></td>
    <td ng-bind="fruit.color"></td>
    ...
</tr>

In the controllers.js file:

// The data initialization part...
$scope.fruits = [
    {
      name: ....,
      color:....
    },
    {
      name: ....,
      color:....
    }
     ...
    ];

// The checked or not data is stored in the object array elements themselves
$scope.fruits.forEach(function(item){
    item.checked = false;
});

// The array to store checked fruit items
$scope.checkedItems = [];

// Every click on any checkbox will trigger the filter to find checked items
$scope.getChecked = function(item){
    $scope.checkedItems = $filter("filter")($scope.fruits,{checked:true});
};

P
Peter Mortensen

Here is yet another solution. The upside of my solution:

It does not need any additional watches (which may have an impact on performance)

It does not require any code in the controller keeping it clean

The code is still somewhat short

It is requires very little code to reuse in multiple places because it is just a directive

Here is the directive:

function ensureArray(o) {
    var lAngular = angular;
    if (lAngular.isArray(o) || o === null || lAngular.isUndefined(o)) {
        return o;
    }
    return [o];
}

function checkboxArraySetDirective() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attrs, ngModel) {
            var name = attrs.checkboxArraySet;

            ngModel.$formatters.push(function(value) {
                return (ensureArray(value) || []).indexOf(name) >= 0;
            });

            ngModel.$parsers.push(function(value) {
                var modelValue = ensureArray(ngModel.$modelValue) || [],
                    oldPos = modelValue.indexOf(name),
                    wasSet = oldPos >= 0;
                if (value) {
                    if (!wasSet) {
                        modelValue = angular.copy(modelValue);
                        modelValue.push(name);
                    }
                } else if (wasSet) {
                    modelValue = angular.copy(modelValue);
                    modelValue.splice(oldPos, 1);
                }
                return modelValue;
            });
        }
    }
}

At the end then just use it like this:

<input ng-repeat="fruit in ['apple', 'banana', '...']" type="checkbox" ng-model="fruits" checkbox-array-set="{{fruit}}" />

And that is all there is. The only addition is the checkbox-array-set attribute.


P
Peter Mortensen

You can combine AngularJS and jQuery. For example, you need to define an array, $scope.selected = [];, in the controller.

<label ng-repeat="item in items">
    <input type="checkbox" ng-model="selected[$index]" ng-true-value="'{{item}}'">{{item}}
</label>

You can get an array owning the selected items. Using method alert(JSON.stringify($scope.selected)), you can check the selected items.


Perfect!... this is the simplest solution using an array not an object
Dont combine Jquery and Angular
This will lead to holes in selected Array. Check this post
v
vijay007
  <div ng-app='app' >
    <div ng-controller='MainCtrl' >
       <ul> 
       <li ng-repeat="tab in data">
         <input type='checkbox' ng-click='change($index,confirm)' ng-model='confirm' />
         {{tab.name}} 
         </li>
     </ul>
    {{val}}
   </div>
 </div>


var app = angular.module('app', []);
 app.controller('MainCtrl',function($scope){
 $scope.val=[];
  $scope.confirm=false;
  $scope.data=[
   {
     name:'vijay'
     },
    {
      name:'krishna'
    },{
      name:'Nikhil'
     }
    ];
    $scope.temp;
   $scope.change=function(index,confirm){
     console.log(confirm);
    if(!confirm){
     ($scope.val).push($scope.data[index]);   
    }
    else{
    $scope.temp=$scope.data[index];
        var d=($scope.val).indexOf($scope.temp);
        if(d!=undefined){
         ($scope.val).splice(d,1);
        }    
       }
     }   
   })

P
Peter Mortensen

Take a look this: checklist-model.

It works with JavaScript arrays, and objects and it can use static HTML checkboxes, without ng-repeat

<label><input type="checkbox" checklist-model="roles" value="admin"> Administrator</label>
<label><input type="checkbox" checklist-model="roles" value="customer"> Customer</label>
<label><input type="checkbox" checklist-model="roles" value="guest"> Guest</label>
<label><input type="checkbox" checklist-model="roles" value="user"> User</label>

And the JavaScript side:

var app = angular.module("app", ["checklist-model"]);
app.controller('Ctrl4a', function($scope) {
    $scope.roles = [];
});

M
Matt Carroll

A simple HTML only way of doing it:


C
Community

Using this example of the @Umur Kontacı, I think in using to catch selected data throughout another object/array, like a edit page.

Catch options at the database

https://i.stack.imgur.com/77E0O.png

Toggle a some option

https://i.stack.imgur.com/erSs6.png

As example, all colors json in below:

{
    "colors": [
        {
            "id": 1,
            "title": "Preto - #000000"
        },
        {
            "id": 2,
            "title": "Azul - #005AB1"
        },
        {
            "id": 3,
            "title": "Azul Marinho - #001A66"
        },
        {
            "id": 4,
            "title": "Amarelo - #FFF100"
        },
        {
            "id": 5,
            "title": "Vermelho - #E92717"
        },
        {
            "id": 6,
            "title": "Verde - #008D2F"
        },
        {
            "id": 7,
            "title": "Cinza - #8A8A8A"
        },
        {
            "id": 8,
            "title": "Prata - #C8C9CF"
        },
        {
            "id": 9,
            "title": "Rosa - #EF586B"
        },
        {
            "id": 10,
            "title": "Nude - #E4CAA6"
        },
        {
            "id": 11,
            "title": "Laranja - #F68700"
        },
        {
            "id": 12,
            "title": "Branco - #FFFFFF"
        },
        {
            "id": 13,
            "title": "Marrom - #764715"
        },
        {
            "id": 14,
            "title": "Dourado - #D9A300"
        },
        {
            "id": 15,
            "title": "Bordo - #57001B"
        },
        {
            "id": 16,
            "title": "Roxo - #3A0858"
        },
        {
            "id": 18,
            "title": "Estampado "
        },
        {
            "id": 17,
            "title": "Bege - #E5CC9D"
        }
    ]
}

And 2 types of data object, array with one object and object containing two/more object data:

Two items selected catched at the database: [{"id":12,"title":"Branco - #FFFFFF"},{"id":16,"title":"Roxo - #3A0858"}]

One item selected catched at the database: {"id":12,"title":"Branco - #FFFFFF"}

And here, my javascript code:

/**
 * Add this code after catch data of database.
 */

vm.checkedColors = [];
var _colorObj = vm.formData.color_ids;
var _color_ids = [];

if (angular.isObject(_colorObj)) {
    // vm.checkedColors.push(_colorObj);
    _color_ids.push(_colorObj);
} else if (angular.isArray(_colorObj)) {
    angular.forEach(_colorObj, function (value, key) {
        // vm.checkedColors.push(key + ':' + value);
        _color_ids.push(key + ':' + value);
    });
}

angular.forEach(vm.productColors, function (object) {
    angular.forEach(_color_ids, function (color) {
        if (color.id === object.id) {
            vm.checkedColors.push(object);
        }
    });
});

/**
 * Add this code in your js function initialized in this HTML page
 */
vm.toggleColor = function (color) {
    console.log('toggleColor is: ', color);

    if (vm.checkedColors.indexOf(color) === -1) {
        vm.checkedColors.push(color);
    } else {
        vm.checkedColors.splice(vm.checkedColors.indexOf(color), 1);
    }
    vm.formData.color_ids = vm.checkedColors;
};

My Html code:

<div class="checkbox" ng-repeat="color in productColors">
    <label>
        <input type="checkbox"
               ng-checked="checkedColors.indexOf(color) != -1"
               ng-click="toggleColor(color)"/>
        <% color.title %>
    </label>
</div>

<p>checkedColors Output:</p>
<pre><% checkedColors %></pre>

[Edit] Refactored code below:

function makeCheckedOptions(objectOptions, optionObj) {
    var checkedOptions = [];
    var savedOptions = [];

    if (angular.isObject(optionObj)) {
        savedOptions.push(optionObj);
    } else if (angular.isArray(optionObj)) {
        angular.forEach(optionObj, function (value, key) {
            savedOptions.push(key + ':' + value);
        });
    }

    angular.forEach(objectOptions, function (object) {
        angular.forEach(savedOptions, function (color) {
            if (color.id === object.id) {
                checkedOptions.push(object);
            }
        });
    });

    return checkedOptions;
}

And call new method as below:

vm.checkedColors = makeCheckedOptions(productColors, vm.formData.color_ids);

That's it!


M
Mahib

I've put an array in the controller.

$scope.statuses = [{ name: 'Shutdown - Reassessment Required' },
    { name: 'Under Construction' },
    { name: 'Administrative Cancellation' },
    { name: 'Initial' },
    { name: 'Shutdown - Temporary' },
    { name: 'Decommissioned' },
    { name: 'Active' },
    { name: 'SO Shutdown' }]

On the markup I've put something like following

<div ng-repeat="status in $scope.statuses">
   <input type="checkbox" name="unit_status" ng-model="$scope.checkboxes[status.name]"> {{status.name}}
   <br>                        
</div>
{{$scope.checkboxes}}

The output was the following, in the controller I just needed to check whether its true or false; true for checked, absent/false for unchecked.

{
"Administrative Cancellation":true,
"Under Construction":true,
"Shutdown - Reassessment Required":true,
"Decommissioned":true,
"Active":true
}

Hope this helps.


C
Community

I think the following way is more clear and useful for nested ng-repeats. Check it out on Plunker.

Quote from this thread:

<html ng-app="plunker">
    <head>
        <title>Test</title>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.min.js"></script>
    </head>

    <body ng-controller="MainCtrl">
        <div ng-repeat="tab in mytabs">

            <h1>{{tab.name}}</h1>
            <div ng-repeat="val in tab.values">
                <input type="checkbox" ng-change="checkValues()" ng-model="val.checked"/>
            </div>
        </div>

        <br>
        <pre> {{selected}} </pre>

            <script>
                var app = angular.module('plunker', []);

                app.controller('MainCtrl', function ($scope,$filter) {
                    $scope.mytabs = [
             {
                 name: "tab1",
                 values: [
                     { value: "value1",checked:false },
                     { value: "value2", checked: false },
                     { value: "value3", checked: false },
                     { value: "value4", checked: false }
                 ]
             },
             {
                 name: "tab2",
                 values: [
                     { value: "value1", checked: false },
                     { value: "value2", checked: false },
                     { value: "value3", checked: false },
                     { value: "value4", checked: false }
                 ]
             }
                    ]
                    $scope.selected = []
                    $scope.checkValues = function () {
                        angular.forEach($scope.mytabs, function (value, index) {
                         var selectedItems = $filter('filter')(value.values, { checked: true });
                         angular.forEach(selectedItems, function (value, index) {
                             $scope.selected.push(value);
                         });

                        });
                    console.log($scope.selected);
                    };
                });
        </script>
    </body>
</html>

P
Peter Mortensen

Here is the jsFillde link for the same, http://jsfiddle.net/techno2mahi/Lfw96ja6/.

This uses the directive which is available for download at http://vitalets.github.io/checklist-model/.

This is the good to have directive as your application will need this functionality much often.

The code is below:

HTML:

<div class="container">
    <div class="ng-scope" ng-app="app" ng-controller="Ctrl1">
        <div class="col-xs-12 col-sm-6">
            <h3>Multi Checkbox List Demo</h3>
            <div class="well">  <!-- ngRepeat: role in roles -->
                <label ng-repeat="role in roles">
                    <input type="checkbox" checklist-model="user.roles" checklist-value="role"> {{role}}
                </label>
            </div>

            <br>
            <button ng-click="checkAll()">check all</button>
            <button ng-click="uncheckAll()">uncheck all</button>
            <button ng-click="checkFirst()">check first</button>
            <div>
                <h3>Selected User Roles </h3>
                <pre class="ng-binding">{{user.roles|json}}</pre>
            </div>

            <br>
            <div><b/>Provided by techno2Mahi</b></div>
        </div>

JavaScript

var app = angular.module("app", ["checklist-model"]);
app.controller('Ctrl1', function($scope) {
  $scope.roles = [
    'guest',
    'user',
    'customer',
    'admin'
  ];
  $scope.user = {
    roles: ['user']
  };
  $scope.checkAll = function() {
    $scope.user.roles = angular.copy($scope.roles);
  };
  $scope.uncheckAll = function() {
    $scope.user.roles = [];
  };
  $scope.checkFirst = function() {
    $scope.user.roles.splice(0, $scope.user.roles.length);
    $scope.user.roles.push('guest');
  };
});

The HTML is not well-formed - there are more opening tags <div> than closing, </div>. Have you left something out?
S
Samantha Guergenenov

Try my baby:

**

myApp.filter('inputSelected', function(){
  return function(formData){
    var keyArr = [];
    var word = [];
    Object.keys(formData).forEach(function(key){
    if (formData[key]){
        var keyCap = key.charAt(0).toUpperCase() + key.slice(1);
      for (var char = 0; char<keyCap.length; char++ ) {
        if (keyCap[char] == keyCap[char].toUpperCase()){
          var spacedLetter = ' '+ keyCap[char];
          word.push(spacedLetter);
        }
        else {
          word.push(keyCap[char]);
        }
      }
    }
    keyArr.push(word.join(''))
    word = [];
    })
    return keyArr.toString();
  }
})

**

Then for any ng-model with checkboxes, it will return a string of all the input you selected:

<label for="Heard about ITN">How did you hear about ITN?: *</label><br>
<label class="checkbox-inline"><input ng-model="formData.heardAboutItn.brotherOrSister" type="checkbox" >Brother or Sister</label>
<label class="checkbox-inline"><input ng-model="formData.heardAboutItn.friendOrAcquaintance" type="checkbox" >Friend or Acquaintance</label>


{{formData.heardAboutItn | inputSelected }}

//returns Brother or Sister, Friend or Acquaintance