Comment répondre aux clics sur une case dans une directive AngularJS?

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 voir, c'est un <table> où chaque ligne peut être sélectionnée individuellement avec sa propre case à cocher, ou toutes les lignes peuvent être sélectionnées à la fois avec une case à cocher maître situé dans le <thead> . Assez classique de l'INTERFACE utilisateur.

Quelle est la meilleure façon d':

  • sélectionner une seule rangée (C.-à-d. lorsque la case à cocher est cochée, ajouter l'id de l'entité sélectionnée à un tableau interne et ajouter une classe CSS à la <tr> contenant l'entité pour refléter son état sélectionné)?
  • sélectionnez Toutes les lignes à la fois? (c'est à dire ne la décrit précédemment actions pour toutes les lignes du <table> )

mon implémentation actuelle est d'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 plus précisément, je me demande:

  • est-ce que le code ci-dessus appartient à un contrôleur ou doit-il aller dans une fonction link ?
  • étant donné que jQuery n'est pas nécessairement présent (AngularJS ne l'exige pas), quelle est la meilleure façon de faire DOM traversal? Sans jQuery, j'ai du mal à sélectionner le parent <tr> d'une case à cocher donnée, ou à sélectionner toutes les cases à cocher dans le modèle.
  • passant $event à updateSelection() ne semble pas très élégant. N'est-il pas une meilleure façon de récupérer l'état (activé/désactivé) d'un élément qui a été le déclic?

Merci.

79
demandé sur AngularChef 2012-08-09 00:41:40

3 réponses

C'est comme ça que je fais ce genre de choses. Angular tend à favoriser la manipulation déclarative du dom plutôt qu'une manipulation impérative(du moins c'est la façon dont 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'entity entier mais il était appelé avec l'id de l'entity seulement, qui est maintenant corrigé

122
répondu Liviu T. 2013-08-27 12:53:43

je préfère utiliser le ngModel et ngChange directives lorsque traitant des cases à cocher . ngModel vous permet de lier l'état vérifié/non vérifié de la case à cocher à une propriété sur l'entité:

<input type="checkbox" ng-model="entity.isChecked">

chaque fois que l'utilisateur vérifie ou décoche la case, la valeur entity.isChecked change aussi.

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" Cochez toutes les réponses", 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 vérifiés et non vérifiés. ngChange est fait pour ce genre de scénario. Il utilise le ngModelController pour la liaison de données (il ajoute un écouteur à l'ngModelController "151970920 tableau. Les auditeurs de ce tableau se font appeler après la valeur du modèle a été définie, éviter 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" Cochez toutes les cases "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

Quelle est la meilleure façon... ajouter une classe CSS à la <tr> contenant l'entité pour refléter son état sélectionné?

si vous utilisez l'approche ngModel pour la liaison de données, tout ce que vous devez faire est d'ajouter la directive ngClass à l'élément <tr> pour ajouter ou supprimer dynamiquement la classe chaque fois que la propriété de l'entité modifications:

<tr ng-repeat="entity in model.entities" ng-class="{selected: entity.isChecked}">

Voir l'intégralité du Plunker ici .

35
répondu Kevin Aenmey 2017-05-23 12:34:50

la réponse de Liviu m'a été extrêmement utile. Espérons que ce n'est pas mauvais, mais j'ai fait un violon qui peut aider quelqu'un d'autre dans l'avenir.

deux pièces importantes qui sont nécessaires sont:

    $scope.entities = [{
    "title": "foo",
    "id": 1
}, {
    "title": "bar",
    "id": 2
}, {
    "title": "baz",
    "id": 3
}];
$scope.selected = [];
11
répondu VBAHole 2013-02-13 22:15:42