Mam dyrektywę AngularJS, która renderuje kolekcję jednostek w następującym szablonie:
<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>
Jak widać, jest to miejsce, w <table>
którym każdy wiersz można wybrać indywidualnie za pomocą własnego pola wyboru lub wszystkie wiersze można zaznaczyć jednocześnie za pomocą głównego pola wyboru znajdującego się w <thead>
. Całkiem klasyczny interfejs użytkownika.
Jaki jest najlepszy sposób, aby:
- Wybierz pojedynczy wiersz (tj. Gdy pole wyboru jest zaznaczone, dodaj identyfikator wybranej jednostki do wewnętrznej tablicy i dodaj klasę CSS do jednostki
<tr>
zawierającej jednostkę, aby odzwierciedlić jej wybrany stan)? - Wybrać wszystkie wiersze jednocześnie? (tj. wykonaj opisane wcześniej czynności dla wszystkich wierszy w
<table>
)
Moja obecna implementacja polega na dodaniu niestandardowego kontrolera do mojej dyrektywy:
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??
};
}
Dokładniej, zastanawiam się:
- Czy powyższy kod należy do kontrolera, czy powinien znajdować się w
link
funkcji? - Biorąc pod uwagę, że jQuery niekoniecznie jest obecny (AngularJS tego nie wymaga), jaki jest najlepszy sposób na przechodzenie przez DOM? Bez jQuery trudno mi po prostu zaznaczyć rodzica
<tr>
danego pola wyboru lub zaznaczyć wszystkie pola wyboru w szablonie. - Przejście
$event
doupdateSelection()
nie wydaje się zbyt eleganckie. Czy nie ma lepszego sposobu na pobranie stanu (zaznaczonego / niezaznaczonego) elementu, który właśnie został kliknięty?
Dziękuję Ci.
źródło
ngChecked
dyrektywie. (Żałuję tylko, że nie możemy uczynić tego kodu nieco mniej szczegółowym.)Wolę używać dyrektyw ngModel i ngChange w przypadku pól wyboru . ngModel umożliwia powiązanie zaznaczonego / niezaznaczonego stanu pola wyboru z właściwością jednostki:
<input type="checkbox" ng-model="entity.isChecked">
Za każdym razem, gdy użytkownik zaznaczy lub usunie zaznaczenie pola wyboru,
entity.isChecked
wartość również się zmieni.Jeśli to wszystko, czego potrzebujesz, nie potrzebujesz nawet dyrektyw ngClick ani ngChange. Ponieważ masz pole wyboru „Zaznacz wszystko”, musisz oczywiście zrobić coś więcej niż tylko ustawić wartość właściwości, gdy ktoś zaznaczy pole wyboru.
Korzystając z ngModel z polem wyboru, najlepiej jest używać ngChange zamiast ngClick do obsługi zaznaczonych i niezaznaczonych zdarzeń. ngChange jest stworzony właśnie dla tego rodzaju scenariusza. Wykorzystuje ngModelController do wiązania danych (dodaje detektor do
$viewChangeListeners
tablicy ngModelControllera . Słuchacze w tej tablicy są wywoływani po ustawieniu wartości modelu, co pozwala uniknąć tego problemu ).<input type="checkbox" ng-model="entity.isChecked" ng-change="selectEntity()">
... iw kontrolerze ...
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; };
Podobnie pole wyboru „Zaznacz wszystko” w nagłówku:
<th> <input type="checkbox" ng-model="model.allItemsSelected" ng-change="selectAll()"> </th>
... i ...
// 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
Jeśli używasz podejścia ngModel do wiązania danych, wszystko, co musisz zrobić, to dodać dyrektywę ngClass do
<tr>
elementu, aby dynamicznie dodawać lub usuwać klasę za każdym razem, gdy zmienia się właściwość jednostki:<tr ng-repeat="entity in model.entities" ng-class="{selected: entity.isChecked}">
Zobacz pełną wersję Plunkera tutaj .
źródło
Odpowiedź Liviu była dla mnie niezwykle pomocna. Mam nadzieję, że to nie jest zła forma, ale zrobiłem skrzypce, które mogą pomóc komuś w przyszłości.
Dwa ważne elementy, które są potrzebne, to:
$scope.entities = [{ "title": "foo", "id": 1 }, { "title": "bar", "id": 2 }, { "title": "baz", "id": 3 }]; $scope.selected = [];
źródło