J'ai un AngularJS directive qui rend une collection d'entités dans le modèle suivant:
<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>
Comme vous pouvez le constater, il s’agit d’un <table>
dans lequel chaque ligne peut être sélectionnée individuellement avec sa propre case à cocher, ou toutes les lignes peuvent être sélectionnées en même temps avec une case à cocher principale située dans le <thead>
. Jolie interface classique.
Quel est le meilleur moyen de:
<tr>
contenant l'entité pour refléter son état sélectionné)?<table>
)Mon implémentation actuelle consiste à ajouter un contrôleur personnalisé à ma 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??
};
}
Plus précisément, je me demande:
link
?<tr>
d'une case à cocher donnée ou à sélectionner toutes les cases à cocher du modèle.$event
à updateSelection()
ne semble pas très élégant. N'y a-t-il pas un meilleur moyen de récupérer l'état (coché/décoché) d'un élément qui vient d'être cliqué?Je vous remercie.
C'est comme ça que je fais ce genre de choses. Angular a tendance à favoriser la manipulation déclarative du dom plutôt qu’une manipulation impérative (du moins c’est comme ça que j’ai joué avec elle).
Le balisage
<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>
Et dans le contrôleur
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()
attend l'entité entière mais elle était appelée avec l'identifiant de l'entité uniquement, ce qui est maintenant corrigé
Je préfère utiliser le ngModel et ngChange directives quand traitant des cases à cocher . ngModel vous permet de lier l'état activé/désactivé de la case à cocher à une propriété de l'entité:
<input type="checkbox" ng-model="entity.isChecked">
Chaque fois que l'utilisateur coche ou décoche la case, la valeur entity.isChecked
change également.
Si c'est tout ce dont vous avez besoin, vous n'avez même pas besoin des directives ngClick ou ngChange. Puisque vous avez la case à cocher "Tout cocher", vous devez évidemment faire plus que simplement définir la valeur de la propriété lorsque quelqu'un coche une case à cocher.
Lorsque vous utilisez ngModel avec une case à cocher, il est préférable d’utiliser ngChange plutôt que ngClick pour gérer les événements cochés et non cochés. ngChange est fait pour ce genre de scénario. Il utilise le ngModelController pour la liaison de données (il ajoute un écouteur au tableau $viewChangeListeners
du ngModelController. Les écouteurs de ce tableau sont appelés après la valeur du modèle a été définie, en évitant ce problème ).
<input type="checkbox" ng-model="entity.isChecked" ng-change="selectEntity()">
... et dans le contrôleur ...
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;
};
De même, la case à cocher "Tout vérifier" dans l'en-tête:
<th>
<input type="checkbox" ng-model="model.allItemsSelected" ng-change="selectAll()">
</th>
... et ...
// 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
Quel est le meilleur moyen de ... ajouter une classe CSS au
<tr>
contenant l'entité pour refléter l'état sélectionné?
Si vous utilisez l'approche ngModel pour la liaison de données, il vous suffit d'ajouter la directive ngClass à l'élément <tr>
pour ajouter ou supprimer de manière dynamique la classe chaque fois que la propriété de l'entité change:
<tr ng-repeat="entity in model.entities" ng-class="{selected: entity.isChecked}">
Voir le complet Plunker here .
La réponse de Liviu m'a été extrêmement utile. J'espère que ce n'est pas une mauvaise forme, mais j'ai fait un violon qui pourrait aider quelqu'un d'autre à l'avenir.
Deux pièces importantes nécessaires sont:
$scope.entities = [{
"title": "foo",
"id": 1
}, {
"title": "bar",
"id": 2
}, {
"title": "baz",
"id": 3
}];
$scope.selected = [];