Javascript 如何响应点击 AngularJS 指令中的复选框?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11872832/
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 to respond to clicks on a checkbox in an AngularJS directive?
提问by AngularChef
I have an AngularJS directivethat renders a collection of entities in the following template:
我有一个 AngularJS指令,它在以下模板中呈现实体集合:
<table class="table">
<thead>
<tr>
<th><input type="checkbox" ng-click="selectAll()"></th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="e in entities">
<td><input type="checkbox" name="selected" ng-click="updateSelection($event, e.id)"></td>
<td>{{e.title}}</td>
</tr>
</tbody>
</table>
As you can see, it's a <table>
where each row can be selected individually with its own checkbox, or all rows can be selected at once with a master checkbox located in the <thead>
. Pretty classic UI.
如您所见,<table>
可以使用自己的复选框单独选择每一行,也可以使用位于<thead>
. 非常经典的用户界面。
What is the best way to:
什么是最好的方法:
- Select a single row (i.e. when the checkbox is checked, add the id of the selected entity to an internal array, and add a CSS class to the
<tr>
containing the entity to reflect its selected state)? - Select all rows at once? (i.e. do the previously described actions for all rows in the
<table>
)
- 选择单行(即选中复选框时,将所选实体的 id 添加到内部数组,并将 CSS 类添加到
<tr>
包含实体以反映其选中状态)? - 一次选择所有行?(即对 中的所有行执行先前描述的操作
<table>
)
My current implementation is to add a custom controller to my directive:
我当前的实现是在我的指令中添加一个自定义控制器:
controller: function($scope) {
// Array of currently selected IDs.
var selected = $scope.selected = [];
// Update the selection when a checkbox is clicked.
$scope.updateSelection = function($event, id) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
if (action == 'add' & selected.indexOf(id) == -1) selected.push(id);
if (action == 'remove' && selected.indexOf(id) != -1) selected.splice(selected.indexOf(id), 1);
// Highlight selected row. HOW??
// $(checkbox).parents('tr').addClass('selected_row', checkbox.checked);
};
// Check (or uncheck) all checkboxes.
$scope.selectAll = function() {
// Iterate on all checkboxes and call updateSelection() on them??
};
}
More specifically, I wonder:
更具体地说,我想知道:
- Does the code above belong in a controller or should it go in a
link
function? - Given that jQuery is not necessarily present (AngularJS doesn't require it), what's the best way to do DOM traversal? Without jQuery, I'm having a hard time just selecting the parent
<tr>
of a given checkbox, or selecting all checkboxes in the template. - Passing
$event
toupdateSelection()
doesn't seem very elegant. Isn't there a better way to retrieve the state (checked/unchecked) of an element that was just clicked?
- 上面的代码属于控制器还是应该放在
link
函数中? - 鉴于 jQuery 不一定存在(AngularJS 不需要它),什么是 DOM 遍历的最佳方式?如果没有 jQuery,我很难选择
<tr>
给定复选框的父级,或者选择模板中的所有复选框。 - 传递
$event
到updateSelection()
似乎不是很优雅。没有更好的方法来检索刚刚单击的元素的状态(选中/未选中)吗?
Thank you.
谢谢你。
回答by Liviu T.
This is the way I've been doing this sort of stuff. Angular tends to favor declarative manipulation of the dom rather than a imperative one(at least that's the way I've been playing with it).
这就是我一直在做这种事情的方式。Angular 倾向于对 dom 进行声明式操作,而不是命令式操作(至少我一直在使用它)。
The markup
标记
<table class="table">
<thead>
<tr>
<th>
<input type="checkbox"
ng-click="selectAll($event)"
ng-checked="isSelectedAll()">
</th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="e in entities" ng-class="getSelectedClass(e)">
<td>
<input type="checkbox" name="selected"
ng-checked="isSelected(e.id)"
ng-click="updateSelection($event, e.id)">
</td>
<td>{{e.title}}</td>
</tr>
</tbody>
</table>
And in the controller
并在控制器中
var updateSelected = function(action, id) {
if (action === 'add' && $scope.selected.indexOf(id) === -1) {
$scope.selected.push(id);
}
if (action === 'remove' && $scope.selected.indexOf(id) !== -1) {
$scope.selected.splice($scope.selected.indexOf(id), 1);
}
};
$scope.updateSelection = function($event, id) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
updateSelected(action, id);
};
$scope.selectAll = function($event) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
for ( var i = 0; i < $scope.entities.length; i++) {
var entity = $scope.entities[i];
updateSelected(action, entity.id);
}
};
$scope.getSelectedClass = function(entity) {
return $scope.isSelected(entity.id) ? 'selected' : '';
};
$scope.isSelected = function(id) {
return $scope.selected.indexOf(id) >= 0;
};
//something extra I couldn't resist adding :)
$scope.isSelectedAll = function() {
return $scope.selected.length === $scope.entities.length;
};
EDIT: getSelectedClass()
expects the entire entity but it was being called with the id of the entity only, which is now corrected
编辑:getSelectedClass()
期望整个实体,但仅使用实体的 id 调用它,现在已更正
回答by Kevin Aenmey
I prefer to use the ngModeland ngChangedirectives when dealing with checkboxes. ngModel allows you to bind the checked/unchecked state of the checkbox to a property on the entity:
在处理复选框时,我更喜欢使用ngModel和ngChange指令。ngModel 允许您将复选框的选中/未选中状态绑定到实体上的属性:
<input type="checkbox" ng-model="entity.isChecked">
Whenever the user checks or unchecks the checkbox the entity.isChecked
value will change too.
每当用户选中或取消选中复选框时,该entity.isChecked
值也会更改。
If this is all you need then you don't even need the ngClick or ngChange directives. Since you have the "Check All" checkbox, you obviously need to do more than just set the value of the property when someone checks a checkbox.
如果这就是你所需要的,那么你甚至不需要 ngClick 或 ngChange 指令。由于您有“全部检查”复选框,因此您显然需要做的不仅仅是在有人选中复选框时设置属性值。
When using ngModel with a checkbox, it's best to use ngChange rather than ngClick for handling checked and unchecked events. ngChange is made for just this kind of scenario. It makes use of the ngModelControllerfor data-binding (it adds a listener to the ngModelController's $viewChangeListeners
array. The listeners in this array get called afterthe model value has been set, avoiding this problem).
当使用带有复选框的 ngModel 时,最好使用 ngChange 而不是 ngClick 来处理选中和未选中的事件。ngChange 就是为这种场景设计的。它利用ngModelController进行数据绑定(它向 ngModelController 的$viewChangeListeners
数组添加了一个监听器。在设置模型值后调用该数组中的监听器,避免了这个问题)。
<input type="checkbox" ng-model="entity.isChecked" ng-change="selectEntity()">
... and in the controller ...
......在控制器中......
var model = {};
$scope.model = model;
// This property is bound to the checkbox in the table header
model.allItemsSelected = false;
// Fired when an entity in the table is checked
$scope.selectEntity = function () {
// If any entity is not checked, then uncheck the "allItemsSelected" checkbox
for (var i = 0; i < model.entities.length; i++) {
if (!model.entities[i].isChecked) {
model.allItemsSelected = false;
return;
}
}
// ... otherwise ensure that the "allItemsSelected" checkbox is checked
model.allItemsSelected = true;
};
Similarly, the "Check All" checkbox in the header:
同样,标题中的“全部检查”复选框:
<th>
<input type="checkbox" ng-model="model.allItemsSelected" ng-change="selectAll()">
</th>
... and ...
... 和 ...
// Fired when the checkbox in the table header is checked
$scope.selectAll = function () {
// Loop through all the entities and set their isChecked property
for (var i = 0; i < model.entities.length; i++) {
model.entities[i].isChecked = model.allItemsSelected;
}
};
CSS
CSS
What is the best way to... add a CSS class to the
<tr>
containing the entity to reflect its selected state?
将 CSS 类添加到
<tr>
包含实体以反映其选定状态的最佳方法是什么?
If you use the ngModel approach for the data-binding, all you need to do is add the ngClassdirective to the <tr>
element to dynamically add or remove the class whenever the entity property changes:
如果您使用 ngModel 方法进行数据绑定,您需要做的就是在元素中添加ngClass指令,以便<tr>
在实体属性更改时动态添加或删除类:
<tr ng-repeat="entity in model.entities" ng-class="{selected: entity.isChecked}">
See the full Plunker here.
回答by VBAHole
Liviu's answer was extremely helpful for me. Hope this is not bad form but i made a fiddlethat may help someone else out in the future.
Liviu 的回答对我非常有帮助。希望这不是糟糕的形式,但我做了一个小提琴,将来可能会帮助其他人。
Two important pieces that are needed are:
需要的两个重要部分是:
$scope.entities = [{
"title": "foo",
"id": 1
}, {
"title": "bar",
"id": 2
}, {
"title": "baz",
"id": 3
}];
$scope.selected = [];