Question Comment lier à la liste des valeurs de checkbox avec AngularJS?


J'ai quelques cases à cocher:

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

Que je voudrais lier à une liste dans mon contrôleur de telle sorte que chaque fois qu'une case à cocher est modifiée le contrôleur maintient une liste de toutes les valeurs vérifiées, par exemple, ['apple', 'pear'].

ng-model semble être seulement capable de lier la valeur d'une seule case à cocher à une variable dans le contrôleur.

Existe-t-il une autre façon de le faire afin que je puisse lier les quatre cases à cocher à une liste dans le contrôleur?


616
2018-01-25 02:36


origine


Réponses:


Il y a deux façons d'aborder ce problème. Utilisez un tableau simple ou un tableau d'objets. Chaque solution a des avantages et des inconvénients. Vous trouverez ci-dessous un pour chaque cas.


Avec un tableau simple comme données d'entrée

Le HTML pourrait ressembler à:

<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>

Et le code de contrôleur approprié serait:

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);
    }
  };
}]);

Avantages: Structure de données simple et basculement par nom est facile à gérer

Les inconvénients: Ajouter / supprimer est fastidieux car deux listes (l'entrée et la sélection) doivent être gérées


Avec un tableau d'objets en tant que données d'entrée

Le HTML pourrait ressembler à:

<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>

Et le code de contrôleur approprié serait:

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);
}]);

Avantages: Ajouter / supprimer est très facile

Les inconvénients: Une structure de données un peu plus complexe et un changement de nom est fastidieux ou nécessite une méthode d'assistance


Démo: http://jsbin.com/ImAqUC/1/


862
2018-01-25 10:40



Une solution simple:

<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


389
2018-05-16 23:42



Voici une petite directive réutilisable qui semble faire ce que vous cherchez à faire. Je l'ai simplement appelé checkList. Il met à jour le tableau lorsque les cases à cocher changent et met à jour les cases à cocher lorsque le tableau change.

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);
    }
  };
});

Voici un contrôleur et une vue qui montre comment vous pouvez l'utiliser.

<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);
  };
});

(Les boutons montrent que la modification du tableau mettra également à jour les cases à cocher.)

Enfin, voici un exemple de la directive en action sur Plunker: http://plnkr.co/edit/3YNLsyoG4PIBW6Kj7dRK?p=preview


80
2018-01-25 10:28



<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);
        }
    };
}

78
2018-01-25 06:57



Basé sur les réponses dans ce fil, j'ai créé liste de contrôle directive qui couvre tous les cas:

  • simple tableau de primitives
  • tableau d'objets (choisir un identifiant ou un objet entier)
  • propriété itération des propriétés

Pour le cas du sujet de départ, ce serait:

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

65
2017-11-13 22:50



le checklist-model La directive sur GitHub par Vitaliy Potapov a absolument travaillé pour moi (en utilisant des objets complexes).

J'ai passé quelques heures à essayer de faire fonctionner les autres solutions sans succès. Excellent travail, vitalets !!


13
2018-03-21 22:48



En utilisant une chaîne de $index peut aider à utiliser un hashmap de valeurs sélectionnées:

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

De cette façon, l'objet ng-model est mis à jour avec la clé représentant l'index.

$scope.someObject = {};

Après un moment $scope.someObject devrait ressembler à quelque chose comme:

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

Cette méthode ne fonctionnera pas pour toutes les situations, mais elle est facile à mettre en œuvre.


11
2017-11-19 16:10



Puisque vous avez accepté une réponse dans laquelle une liste n'a pas été utilisée, je suppose que la réponse à ma question de commentaire est «Non, il ne doit pas s'agir d'une liste». J'ai aussi eu l'impression que vous étiez peut-être en train de déchirer le côté HTML du serveur, puisque "checked" est présent dans votre exemple de HTML (cela ne serait pas nécessaire si ng-model était utilisé pour modéliser vos cases à cocher).

Quoi qu'il en soit, voici ce que j'avais à l'esprit lorsque j'ai posé la question, en supposant également que vous générez le côté serveur HTML:

<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 permet au HTML généré côté serveur de définir certaines cases à cocher.

Violon.


8
2018-01-25 19:45



Je pense que la solution de contournement la plus simple serait d'utiliser 'select' avec 'multiple' spécifié:

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

Sinon, je pense que vous devrez traiter la liste pour construire la liste (par $watch()dans le tableau array bind avec des cases à cocher).


6
2018-01-25 04:23



J'ai adapté la réponse acceptée de Yoshi pour traiter des objets complexes (au lieu des cordes).

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);
        }
    };
}

Exemple de travail: http://jsfiddle.net/tCU8v/


5
2017-10-30 13:14



Une autre directive simple pourrait être:

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);
        });
    }
};
}]);

Le controlle:

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 = [];
    };
}]);

Et le 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>

J'inclus également un Plunker: http://plnkr.co/edit/XnFtyij4ed6RyFwnFN6V?p=preview


5
2018-03-12 21:13