Tengo una directiva AngularJS que representa una colección de entidades en la siguiente plantilla:
<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>
Como puede ver, es un lugar <table>
donde cada fila se puede seleccionar individualmente con su propia casilla de verificación, o todas las filas se pueden seleccionar a la vez con una casilla de verificación maestra ubicada en el <thead>
. Interfaz de usuario bastante clásica.
Cuál es la mejor forma de:
- Seleccione una sola fila (es decir, cuando la casilla de verificación esté marcada, agregue la identificación de la entidad seleccionada a una matriz interna y agregue una clase CSS al que
<tr>
contiene la entidad para reflejar su estado seleccionado). - ¿Seleccionar todas las filas a la vez? (es decir, realice las acciones descritas anteriormente para todas las filas del
<table>
)
Mi implementación actual es agregar un controlador personalizado a mi directiva:
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??
};
}
Más específicamente, me pregunto:
- ¿El código anterior pertenece a un controlador o debería ir en un
link
función? - Dado que jQuery no está necesariamente presente (AngularJS no lo requiere), ¿cuál es la mejor manera de hacer un recorrido DOM? Sin jQuery, estoy teniendo dificultades para seleccionar el padre
<tr>
de una casilla de verificación determinada o seleccionar todas las casillas de verificación en la plantilla. - Pasar
$event
aupdateSelection()
no parece muy elegante. ¿No hay una mejor manera de recuperar el estado (marcado / desmarcado) de un elemento en el que se acaba de hacer clic?
Gracias.
fuente
ngChecked
directiva. (Lo único que lamento es que no podamos hacer que este código sea un poco menos detallado.)Prefiero usar las directivas ngModel y ngChange cuando trato con casillas de verificación . ngModel le permite vincular el estado marcado / no marcado de la casilla de verificación a una propiedad de la entidad:
<input type="checkbox" ng-model="entity.isChecked">
Siempre que el usuario marque o desmarque la casilla de verificación, el
entity.isChecked
valor también cambiará.Si esto es todo lo que necesita, entonces ni siquiera necesita las directivas ngClick o ngChange. Dado que tiene la casilla de verificación "Marcar todo", obviamente necesita hacer más que simplemente establecer el valor de la propiedad cuando alguien marca una casilla de verificación.
Al usar ngModel con una casilla de verificación, es mejor usar ngChange en lugar de ngClick para manejar eventos marcados y no marcados. ngChange está diseñado para este tipo de escenario. Hace uso de ngModelController para el enlace de datos (agrega un oyente a la matriz de ngModelController
$viewChangeListeners
. Los oyentes en esta matriz son llamados después de que se haya establecido el valor del modelo, evitando este problema ).<input type="checkbox" ng-model="entity.isChecked" ng-change="selectEntity()">
... y en el controlador ...
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; };
Del mismo modo, la casilla de verificación "Marcar todo" en el encabezado:
<th> <input type="checkbox" ng-model="model.allItemsSelected" ng-change="selectAll()"> </th>
... y ...
// 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
Si usa el enfoque ngModel para el enlace de datos, todo lo que necesita hacer es agregar la directiva ngClass al
<tr>
elemento para agregar o eliminar dinámicamente la clase siempre que cambie la propiedad de la entidad:<tr ng-repeat="entity in model.entities" ng-class="{selected: entity.isChecked}">
Vea el Plunker completo aquí .
fuente
La respuesta de Liviu fue de gran ayuda para mí. Espero que esto no sea una mala forma, pero hice un violín que puede ayudar a alguien más en el futuro.
Dos piezas importantes que se necesitan son:
$scope.entities = [{ "title": "foo", "id": 1 }, { "title": "bar", "id": 2 }, { "title": "baz", "id": 3 }]; $scope.selected = [];
fuente