¿Cómo me uno a la lista de valores de casillas de verificación con AngularJS?

670

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 cada vez que se cambie una casilla de verificación, el controlador mantenga una lista de todos los valores marcados, por ejemplo ['apple', 'pear'] ,.

Parece que ng-model solo puede vincular el valor de una única casilla de verificación a una variable en el controlador.

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

nickponline
fuente
23
¿Tiene que ser una lista? ¿Funcionaría un objeto ?: <input type='checkbox' ng-model="checkboxes.apple">, etc. El modelo sería: {"manzana": verdadero, "naranja": falso, "pera": verdadero, "naartjie": verdadero}
Mark Rajcok
2
Prueba la directiva en este Repo
Vikas Gautam
1
Asegúrese de mirar más allá de la respuesta aceptada. Hay otra respuesta que es, en mi opinión, mucho más elegante.
Jason Swett
3
naartjie!? Eso solo te delata boet! : D
Piotr Kula
1
@ppumkin jeje acabo de ver esto. Tienes razón: D
nickponline

Respuestas:

927

Hay dos formas de abordar este problema. Utilice una matriz simple o una matriz de objetos. Cada solución tiene sus pros y sus contras. 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 del 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 / quitar es engorroso ya que se deben administrar dos listas (la entrada y la selección)


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 del 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 / quitar es muy fácil

Contras : la estructura de datos algo más compleja y alternar por nombre es engorrosa o requiere un método auxiliar


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

Yoshi
fuente
10
Para su información, en lugar de inyectar $ filter, puede inyectar filterFilter y luego usarlo de la siguiente manera: return filterFilter ($ scope.fruits, {marcado: verdadero}); Los filtros integrados y personalizados se registran con el inyector $ con el nombre filterNameFilter ("filterName" debe estar en cursiva) - $ filterProvider docs
Mark Rajcok
24
value="{{fruit.name}}"y ng-checked="fruit.checked"son superfluos, ya que se usa ng-model.
Mark Rajcok
3
Noté que no hay necesidad de especificar "marcado" en el modelo, Angular establecerá la propiedad automáticamente :)
daveoncode
3
Debería usar ng-change en lugar de ng-click porque trata mejor los casos extremos.
amccausl
2
@ViktorMolokostov Eso sería útil si presentara el formulario tradicionalmente . Es decir, publicarlo en el controlador de acciones (algún script del lado del servidor). Con php, un elemento de formulario con un nombre como ese (usando corchetes) crea una matriz en los datos de la solicitud. De esta manera, podría manejar fácilmente las frutas seleccionadas.
Yoshi
406

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

Kolypto
fuente
57
@kolypto: esta es definitivamente la respuesta. Lo reescribí para las personas (como yo) que están trabajando con objetos: plnkr.co/edit/cqsADe8lKegsBMgWMyB8?p=preview
Kyle
55
Lo hago igual que tú, pero ¿qué hace habilitado en (color,enabled) in colors?
Sebastian
3
@Sebastian, dado que colorses un objeto, cuando lo iteras, obtienes pares de (key,value).
kolypto
10
¡Aunque me gusta mucho esta respuesta! Creo que hay un problema importante con el uso de objetos como fuente de datos. Es decir, debido a que, por definición, el orden de las propiedades de los objetos no está definido, no se puede proporcionar un orden definido al mostrar las casillas de verificación. Todavía +1;)
Yoshi
2
colorsdebe nombrarse isSelected, es mucho más fácil de leer isSelected[color]quecolors[color]
Dmitri Zaitsev
87
<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);
        }
    };
}
Umur Kontacı
fuente
2
Me encanta lo simple que es esto, exactamente lo que estoy buscando (aunque tengo que admitir que la directiva @vitalets es increíble). He modificado un poco el código de Umur para crear este violín: jsfiddle.net/samurai_jane/9mwsbfuc
samurai_jane
¡Hago mías las palabras de Samurai Jane! ¡Qué simple fue mostrar lo que necesitaba! :)
Francis Rodrigues
81

Aquí hay una pequeña y rápida directiva reutilizable que parece hacer lo que estás buscando hacer. Simplemente lo he llamado checkList. Actualiza la matriz cuando cambian las casillas de verificación, 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, aquí hay un ejemplo de la directiva en acción en Plunker: http://plnkr.co/edit/3YNLsyoG4PIBW6Kj7dRK?p=preview

Michelle Tilley
fuente
2
Gracias Brandon, esto hizo exactamente lo que quería (y exactamente lo que pedía la pregunta también, a diferencia de las otras respuestas). El único ajuste que hice fue cambiar su "elem.on ('change', function () ..." a "elem.bind ('change', function () ..." para eliminar la dependencia de jQuery .
Jonathan Moffatt
Esto es bastante bueno, pero de alguna manera destruye mi capacidad de usar ng-disabled :( ¿Hay alguna forma de que pueda arreglar eso?
Nikolaj Dam Larsen
Super útil! ¡E incluso funcionó para mí con objetos en lugar de matrices tanto para la lista de origen como para la lista de datos!
SteveShaffer
Estoy de acuerdo con todos ¡Este es el más útil e indudablemente reutilizable! Gracias por el buen trabajo. :)
maksbd19 01 de
2
Si tiene problemas con AngularJS> = 1.4.4, consulte github.com/angular/angular.js/issues/13037 : reemplace value: '@'porvalue: '=ngValue'
tanguy_k
66

En base a las respuestas en este hilo, he creado una directiva de modelo de lista de verificación que cubre todos los casos:

  • matriz simple de primitivas
  • matriz de objetos (pick id u objeto completo)
  • iteración de propiedades de 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>
vitalets
fuente
Eso se parece a lo que necesito. ¿Hay alguna posibilidad de que pueda explicar cómo usarlo al obtener los datos de forma asincrónica? Esa parte es confusa para mí.
Dan Cancro
Después de obtener los datos de forma asincrónica, simplemente modifique el modelo de la lista de control en el alcance, en el ejemplo anterior selectedFruits.
Adrian Ber
11

Usar una cadena de $indexpuede 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.someObjectdebería verse algo así como:

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

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

usuario2479438
fuente
Esta es una solución muy elegante y se adapta a mi caso (usando AJAX)
Stephan Ryer
usa el método del beso
Geomorillo
8

Como aceptó 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 estaba leyendo el lado del servidor HTML, ya que "marcado" está presente en su HTML de muestra (esto no sería necesario si se usara ng-model para modelar sus casillas de verificación).

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

Violín .

Mark Rajcok
fuente
8

Creo que la solución más fácil sería usar 'select' con 'multiple' especificado:

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

De lo contrario, creo que tendrá que procesar la lista para construirla (al $watch()vincular la matriz del modelo con casillas de verificación).

Tosh
fuente
3
Él está pidiendo una lista de casillas de verificación y, sin embargo, usted le está diciendo que seleccione con opciones. Lo cual es completamente diferente.
CrazySabbath
@CrazySabbath: sin embargo, no está entendiendo que lo que sugiere una solución alternativa y esta respuesta ha ayudado a otras 6 personas como una "solución alternativa"
curiousBoy
5

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/

Adán
fuente
1
¡Nunca debería tener un <input type="checkbox">sin envoltura o combinación <label>! Ahora sus usuarios tienen que hacer clic en la casilla de verificación real en lugar del texto al lado de la casilla de verificación, lo cual es mucho más difícil y tiene una mala usabilidad.
Scott
5

Otra directiva simple podría ser:

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

El controlador:

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

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

También estoy incluyendo un Plunker: http://plnkr.co/edit/XnFtyij4ed6RyFwnFN6V?p=preview

Adrian Stanescu
fuente
5

La siguiente solución parece una buena opción,

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

Y en el modelo del controlador el valor fruitsserá así

$scope.fruits = [
  {
    "name": "apple",
    "checked": true
  },
  {
    "name": "orange"
  },
  {
    "name": "grapes",
    "checked": true
  }
];
Mohammed Safeer
fuente
cuanto más miro estos ejemplos, parece que tendré que mapear mi matriz en una matriz de objetos.
Winnemucca
4

No tienes que escribir todo ese código. AngularJS mantendrá el modelo y las casillas de verificación sincronizadas simplemente usando ngTrueValue y ngFalseValue

Codepen aquí: http://codepen.io/paulbhartzog/pen/kBhzn

Fragmento de código:

<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>
Paul B. Hartzog
fuente
Esto no es lo que está pidiendo OP.
bfontaine
Vincular las casillas de verificación a una lista es lo que se pidió y lo que hice. La matriz se puede modificar para adaptarse a la aplicación. El punto es que las casillas de verificación están vinculadas. ngTrueValue y ngFalseValue también se pueden usar para asignar a una segunda matriz que solo enumera otros atributos, como los nombres.
Paul B. Hartzog
OP quiere una lista de valores marcados, no una lista de todos los valores, marcados y sin marcar.
bfontaine
4

Hay una manera de trabajar directamente en la matriz y usar ng-model al mismo tiempo ng-model-options="{ getterSetter: true }".

El truco es usar una función getter / setter en su modelo ng. De esta manera, puede usar una matriz como su modelo real y "falsificar" los booleanos en el modelo de entrada:

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

PRUEBA No debe usar este método si sus matrices son grandes, como myGetterSetterse llamará muchas veces.

Para obtener más información al respecto, consulte https://docs.angularjs.org/api/ng/directive/ngModelOptions .

david
fuente
3

Me gusta la respuesta de Yoshi. Lo mejoré para que pueda usar la misma función para varias listas.

<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

Bikey
fuente
3

Si tiene varias casillas de verificación en el mismo formulario

El código del controlador

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

Ver código

<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>        
Rama Krshna Ila
fuente
3

Inspirado en la publicación de Yoshi arriba. Aquí está el 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;  
}
<!DOCTYPE html>
<html>

   <head>
      <link rel="stylesheet" href="style.css" />
   </head>

   <body ng-app="APP" ng-controller="demoCtrl as dc">
      <h1>checkbox-select demo</h1>
      
      <h4>Multiple Selections</h4>
      <label ng-repeat="thing in dc.list">
         <input 
            type="checkbox" 
            ng-checked="dc.multipleSelections.indexOf(thing) > -1"
            ng-click="dc.addOrRemove(dc.multipleSelections, thing, true)"
         > {{thing}}
      </label>
      
      <p>
         dc.multipleSelections :- {{dc.multipleSelections}}
      </p>
      
      <hr>
      
      <h4>Individual Selections</h4>
      <label ng-repeat="thing in dc.list">
         <input 
            type="checkbox" 
            ng-checked="dc.individualSelections.indexOf(thing) > -1"
            ng-click="dc.addOrRemove(dc.individualSelections, thing, false)"
         > {{thing}}
      </label>
      
      <p>
         dc.invidualSelections :- {{dc.individualSelections}}
      </p>
      
      <script data-require="[email protected]" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
      <script data-require="[email protected]" data-semver="1.5.6" src="https://code.angularjs.org/1.5.6/angular.min.js"></script>
      <script src="script.js"></script>
   </body>

</html>

Vikas Gautam
fuente
3

Basado en mi otra publicación aquí , he hecho una directiva reutilizable.

Echa un vistazo al repositorio de GitHub

(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;  
}
<!DOCTYPE html>
<html>

   <head>
      <link rel="stylesheet" href="style.css" />
      
   </head>
   
   <body ng-app="APP" ng-controller="demoCtrl as dc">
      <h1>checkbox-select demo</h1>
      
      <h4>Multiple Selections</h4>
      <label ng-repeat="thing in dc.list">
         <input type="checkbox" checkbox-model="dc.multipleSelections" checkbox-value="thing" multiple>
         {{thing}}
      </label>
      <p>dc.multipleSelecitons:- {{dc.multipleSelections}}</p>
      
      <h4>Individual Selections</h4>
      <label ng-repeat="thing in dc.list">
         <input type="checkbox" checkbox-model="dc.individualSelections" checkbox-value="thing">
         {{thing}}
      </label>
      <p>dc.individualSelecitons:- {{dc.individualSelections}}</p>
      
      <script data-require="[email protected]" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
      <script data-require="[email protected]" data-semver="1.5.6" src="https://code.angularjs.org/1.5.6/angular.min.js"></script>
      <script src="script.js"></script>
   </body>

</html>

Vikas Gautam
fuente
3

En el HTML (suponiendo que las casillas de verificación estén en la primera columna de cada fila de una tabla).

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

En el controllers.jsarchivo:

// 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});
};
lqt0223
fuente
3

Aquí hay otra solución más. La ventaja de mi solución:

  • No necesita ningún reloj adicional (lo que puede tener un impacto en el rendimiento)
  • No requiere ningún código en el controlador para mantenerlo limpio
  • El código aún es algo corto.
  • Requiere muy poco código para reutilizar en múltiples lugares porque es solo una directiva

Aquí está la directiva:

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

Al final, simplemente úsalo así:

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

Y eso es todo lo que hay. La única adición es el checkbox-array-setatributo.

yanqui
fuente
3

Puede combinar AngularJS y jQuery. Por ejemplo, debe definir una matriz,, $scope.selected = [];en el controlador.

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

Puede obtener una matriz propietaria de los elementos seleccionados. Usando el método alert(JSON.stringify($scope.selected)), puede verificar los elementos seleccionados.

Dahai
fuente
¡Perfecto! ... esta es la solución más simple usando una matriz, no un objeto
Mario Campa
3
No combine Jquery y Angular
Jens Alenius
Esto conducirá a agujeros en la matriz seleccionada. Mira esta publicación
Vikas Gautam
2
  <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);
        }    
       }
     }   
   })
vijay007
fuente
1

Echa un vistazo a esto: lista de verificación-modelo .

Funciona con matrices de JavaScript y objetos y puede usar casillas de verificación HTML estáticas, sin 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>

Y el lado de JavaScript:

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

Una forma simple de hacerlo en HTML:

<input type="checkbox"
       ng-checked="fruits.indexOf('apple') > -1"
       ng-click="fruits.indexOf('apple') > -1 ? fruits.splice(fruits.indexOf('apple'), 1) : fruits.push('apple')">
<input type="checkbox"
       ng-checked="fruits.indexOf('orange') > -1"
       ng-click="fruits.indexOf('orange') > -1 ? fruits.splice(fruits.indexOf('orange'), 1) : fruits.push('orange')">
<input type="checkbox"
       ng-checked="fruits.indexOf('pear') > -1"
       ng-click="fruits.indexOf('pear') > -1 ? fruits.splice(fruits.indexOf('pear'), 1) : fruits.push('pear')">
<input type="checkbox"
       ng-checked="fruits.indexOf('naartjie') > -1"
       ng-click="fruits.indexOf('apple') > -1 ? fruits.splice(fruits.indexOf('apple'), 1) : fruits.push('naartjie')">

Matt Carroll
fuente
1

Usando este ejemplo de @Umur Kontacı, pienso en usar para capturar datos seleccionados en otro objeto / matriz, como una página de edición.

Opciones de captura en la base de datos

ingrese la descripción de la imagen aquí

Alternar una opción

ingrese la descripción de la imagen aquí

Como ejemplo, todos los colores json a continuación:

{
    "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"
        }
    ]
}

Y 2 tipos de objeto de datos, arraycon un objeto y que objectcontienen dos / más datos de objeto:

  • Dos elementos seleccionados capturados en la base de datos:

    [{"id":12,"title":"Branco - #FFFFFF"},{"id":16,"title":"Roxo - #3A0858"}]
  • Un elemento seleccionado capturado en la base de datos:

    {"id":12,"title":"Branco - #FFFFFF"}

Y aquí, mi código javascript:

/**
 * 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;
};

Mi código html:

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

[Editar] Código refactorizado a continuación:

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

Y llame al nuevo método de la siguiente manera:

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

¡Eso es!

Francis Rodrigues
fuente
1

He puesto una matriz en el controlador.

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

En el marcado he puesto algo como seguir

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

La salida fue la siguiente, en el controlador solo necesitaba verificar si es verdadero o falso; verdadero para marcado, ausente / falso para no marcado.

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

Espero que esto ayude.

Mahib
fuente
0

Creo que la siguiente forma es más clara y útil para ng-repeticiones anidadas. Compruébalo en Plunker .

Cita de este hilo :

<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>
Oğuz Can Sertel
fuente
0

Aquí está el enlace jsFillde para el mismo, http://jsfiddle.net/techno2mahi/Lfw96ja6/ .

Utiliza la directiva que está disponible para descargar en http://vitalets.github.io/checklist-model/ .

Es bueno tener directivas ya que su aplicación necesitará esta funcionalidad con mucha frecuencia.

El código está abajo:

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');
  };
});
Mahendra
fuente
El HTML no está bien formado - hay más etiquetas de apertura <div>que de cierre, </div>. ¿Has dejado algo afuera?
Peter Mortensen
0

Prueba mi bebe:

** **

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

** **

Luego, para cualquier modelo ng con casillas de verificación, devolverá una cadena de todas las entradas que seleccionó:

<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
Samantha Guergenenov
fuente