Javascript 如何使用 AngularJS 绑定到复选框值列表?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14514461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I bind to list of checkbox values with AngularJS?
提问by nickponline
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'].
我想绑定到我的控制器中的一个列表,这样无论何时更改复选框,控制器都会维护所有选中值的列表,例如['apple', 'pear'].
ng-model seems to only be able to bind the value of one single checkbox to a variable in the controller.
ng-model 似乎只能将一个复选框的值绑定到控制器中的一个变量。
Is there another way to do it so that I can bind the four checkboxes to a list in the controller?
是否有另一种方法可以将四个复选框绑定到控制器中的列表?
回答by Yoshi
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:
HTML 可能如下所示:
<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:
HTML 可能如下所示:
<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
缺点:有点复杂的数据结构和按名称切换很麻烦或需要辅助方法
回答by kolypto
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>
回答by Umur Kontac?
<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);
}
};
}
回答by 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.
这是一个快速的小可重用指令,它似乎可以完成您想要做的事情。我只是简单地称之为checkList。它在复选框更改时更新数组,并在数组更改时更新复选框。
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
最后,这是一个在 Plunker 上运行的指令示例:http://plnkr.co/edit/3YNLsyoG4PIBW6Kj7dRK?p=preview
回答by vitalets
Based on answers in this thread I've created checklist-modeldirective that covers all cases:
基于此线程中的答案,我创建了涵盖所有情况的checklist-model指令:
- simple array of primitives
- array of objects (pick id or whole object)
- object properties iteration
- 简单的基元数组
- 对象数组(选择 id 或整个对象)
- 对象属性迭代
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>
回答by user2479438
Using a string of $indexcan help to use a hashmap of selected values:
使用字符串$index可以帮助使用选定值的哈希图:
<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.
通过这种方式,ng-model 对象会使用表示索引的键进行更新。
$scope.someObject = {};
After a while $scope.someObjectshould look something like:
一段时间后$scope.someObject应该看起来像:
$scope.someObject = {
0: true,
4: false,
1: true
};
This method won't work for all situations, but it is easy to implement.
这种方法并不适用于所有情况,但很容易实现。
回答by Tosh
I think the easiest workaround would be to use 'select' with 'multiple' specified:
我认为最简单的解决方法是使用指定了 'multiple' 的 'select':
<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).
否则,我认为您必须处理列表以构建列表(通过$watch()使用复选框绑定模型数组)。
回答by 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).
由于您接受了未使用列表的答案,因此我假设我的评论问题的答案是“不,它不必是列表”。我也有这样的印象,也许您正在渲染 HTML 服务器端,因为“已检查”存在于您的示例 HTML 中(如果使用 ng-model 对您的复选框进行建模,则不需要这样做)。
Anyway, here's what I had in mind when I asked the question, also assuming you were generating the HTML server-side:
无论如何,这就是我提出问题时的想法,还假设您正在生成 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 allows server-side generated HTML to initially set certain checkboxes.
ng-init 允许服务器端生成的 HTML 初始设置某些复选框。
小提琴。
回答by Adam
I have adapted Yoshi's accepted answer to deal with complex objects (instead of strings).
我已经改编了 Yoshi 接受的答案来处理复杂的对象(而不是字符串)。
HTML
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
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/
工作示例:http: //jsfiddle.net/tCU8v/
回答by 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:
和 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
我还包括一个Plunker:http://plnkr.co/edit/XnFtyij4ed6RyFwnFN6V?p=preview

