Pregunta ¿Cómo puedo vincular a la lista de valores de casilla de verificación con AngularJS?


Tengo algunas casillas de verificación:

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

Que me gustaría vincular a una lista en mi controlador de modo que siempre que se cambie una casilla de verificación, el controlador mantenga una lista de todos los valores marcados, por ejemplo, ['apple', 'pear'].

ng-model parece ser capaz de vincular el valor de una sola casilla de verificación a una variable en el controlador.

¿Hay alguna otra manera de hacerlo para poder vincular las cuatro casillas de verificación a una lista en el controlador?


616
2018-01-25 02:36


origen


Respuestas:


Hay dos formas de abordar este problema. Utilice una matriz simple o una matriz de objetos. Cada solución tiene sus ventajas y desventajas. A continuación encontrará uno para cada caso.


Con una matriz simple como datos de entrada

El HTML podría verse así:

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

Y el código de controlador apropiado sería:

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: Estructura de datos simple y alternar por nombre es fácil de manejar

Contras: Agregar / eliminar es engorroso ya que dos listas (la entrada y la selección) deben ser administradas


Con una matriz de objetos como datos de entrada

El HTML podría verse así:

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

Y el código de controlador apropiado sería:

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: Agregar / eliminar es muy fácil

Contras: Estructura de datos algo más compleja y alternar por nombre es engorroso o requiere un método de ayuda


Manifestación: http://jsbin.com/ImAqUC/1/


862
2018-01-25 10:40



Una solución 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



Aquí hay una pequeña y rápida directiva reutilizable que parece hacer lo que usted está buscando hacer. Simplemente lo llamé checkList. Actualiza la matriz cuando las casillas de verificación cambian y actualiza las casillas de verificación cuando cambia la matriz.

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

Aquí hay un controlador y una vista que muestra cómo puede usarlo.

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

(Los botones demuestran que cambiar la matriz también actualizará las casillas de verificación).

Finalmente, he aquí un ejemplo de la directiva en acción sobre 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



Basado en las respuestas en este hilo que he creado lista de verificación-modelo directiva que cubre todos los casos:

  • simple conjunto de primitivos
  • array de objetos (pick id o objeto entero)
  • iteración de propiedades del objeto

Para el caso de inicio de tema sería:

<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



los checklist-model La directiva sobre GitHub de Vitaliy Potapov funcionó absolutamente para mí (utilizando objetos complejos).

Pasé unas horas intentando que las otras soluciones funcionaran sin suerte. ¡Buen trabajo, vitalets!


13
2018-03-21 22:48



Usando una cadena de $index puede ayudar a usar un hashmap de valores seleccionados:

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

De esta forma, el objeto ng-model se actualiza con la clave que representa el índice.

$scope.someObject = {};

Después de un tiempo $scope.someObject debería verse algo así como:

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

Este método no funcionará para todas las situaciones, pero es fácil de implementar.


11
2017-11-19 16:10



Como aceptaste una respuesta en la que no se utilizó una lista, asumiré que la respuesta a mi pregunta de comentario es "No, no tiene que ser una lista". También tuve la impresión de que tal vez estabas rasgando el lado del servidor HTML, ya que "marcado" está presente en tu HTML de muestra (esto no sería necesario si ng-model se usara para modelar tus casillas de verificación).

De todos modos, esto es lo que tenía en mente cuando hice la pregunta, también suponiendo que estuvieras generando el lado del servidor 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 permite que el HTML generado en el servidor establezca inicialmente ciertas casillas de verificación.

Violín.


8
2018-01-25 19:45



Creo que la solución más fácil sería usar 'seleccionar' con 'múltiple' especificado:

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

De lo contrario, creo que tendrás que procesar la lista para construir la lista (por $watch()ing la matriz de modelo se vincula con casillas de verificación).


6
2018-01-25 04:23



He adaptado la respuesta aceptada de Yoshi para tratar con objetos complejos (en lugar de cadenas).

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

Ejemplo de trabajo: http://jsfiddle.net/tCU8v/


5
2017-10-30 13:14