How to respond to clicks on a checkbox in an AngularJS directive?

后端 未结 3 1933
孤独总比滥情好
孤独总比滥情好 2020-12-02 05:03

I have an AngularJS directive that renders a collection of entities in the following template:

相关标签:
3条回答
  • 2020-12-02 05:31

    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).

    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

    0 讨论(0)
  • 2020-12-02 05:33

    Liviu's answer was extremely helpful for me. Hope this is not bad form but i made a fiddle that may help someone else out in the future.

    Two important pieces that are needed are:

        $scope.entities = [{
        "title": "foo",
        "id": 1
    }, {
        "title": "bar",
        "id": 2
    }, {
        "title": "baz",
        "id": 3
    }];
    $scope.selected = [];
    
    0 讨论(0)
  • 2020-12-02 05:40

    I prefer to use the ngModel and ngChange directives when dealing with checkboxes. ngModel allows you to bind the checked/unchecked state of the checkbox to a property on the entity:

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

    Whenever the user checks or unchecks the checkbox the entity.isChecked value will change too.

    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.

    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 ngModelController for data-binding (it adds a listener to the ngModelController's $viewChangeListeners array. The listeners in this array get called after the model value has been set, avoiding this problem).

    <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

    What is the best way to... add a CSS class to the <tr> containing the entity to reflect its selected state?

    If you use the ngModel approach for the data-binding, all you need to do is add the ngClass directive to the <tr> element to dynamically add or remove the class whenever the entity property changes:

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

    See the full Plunker here.

    0 讨论(0)
提交回复
热议问题