Làm thế nào để phản hồi các nhấp chuột vào hộp kiểm trong chỉ thị AngularJS?


79

Tôi có một chỉ thị AngularJS hiển thị một tập hợp các thực thể trong mẫu sau:

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

Như bạn có thể thấy, đó là <table>nơi mỗi hàng có thể được chọn riêng lẻ bằng hộp kiểm của riêng nó hoặc tất cả các hàng có thể được chọn cùng một lúc với hộp kiểm chính nằm trong <thead>. Giao diện người dùng khá cổ điển.

Cách tốt nhất để:

  • Chọn một hàng duy nhất (tức là khi hộp kiểm được chọn, hãy thêm id của thực thể đã chọn vào một mảng bên trong và thêm một lớp CSS vào <tr>thực thể chứa để phản ánh trạng thái đã chọn của nó)?
  • Chọn tất cả các hàng cùng một lúc? (tức là thực hiện các hành động được mô tả trước đó cho tất cả các hàng trong <table>)

Cách triển khai hiện tại của tôi là thêm bộ điều khiển tùy chỉnh vào chỉ thị của tôi:

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??
    };
}

Cụ thể hơn, tôi tự hỏi:

  • Đoạn mã trên có nằm trong một bộ điều khiển hay nó phải đi trong một linkhàm?
  • Cho rằng jQuery không nhất thiết phải xuất hiện (AngularJS không yêu cầu), cách tốt nhất để thực hiện duyệt DOM là gì? Nếu không có jQuery, tôi gặp khó khăn khi chỉ chọn <tr>hộp kiểm cha của một hộp kiểm nhất định hoặc chọn tất cả các hộp kiểm trong mẫu.
  • Đi qua $eventđể updateSelection()không có vẻ rất thanh lịch. Không có cách nào tốt hơn để truy xuất trạng thái (đã chọn / bỏ chọn) của một phần tử vừa được nhấp?

Cảm ơn bạn.

Câu trả lời:


122

Đây là cách tôi đã làm những thứ như thế này. Angular có xu hướng ủng hộ thao tác khai báo đối với dom hơn là bắt buộc (ít nhất đó là cách tôi đã chơi với nó).

Đánh dấu

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

Và trong bộ điều khiển

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 : yêu getSelectedClass()cầu toàn bộ thực thể nhưng nó chỉ được gọi với id của thực thể, hiện đã được sửa


Cảm ơn, Liviu! Điều đó hoạt động và nó đã giúp. Và nhờ bạn, tôi đã biết về ngCheckedchỉ thị. (Điều hối tiếc duy nhất của tôi là chúng tôi không thể làm cho mã này ít dài dòng hơn một chút.)
AngularChef

1
Đừng nghĩ về nó là dài dòng, hãy nghĩ về nó ở khía cạnh tách rời các mối quan tâm. Các mô hình dữ liệu của bạn không nên biết về cách nó được trình bày. Hãy nhớ trong bộ điều khiển không có đề cập đến của tr hoặc td. tối đa nó có chứa hộp kiểm nhưng điều đó cũng có thể bị loại ra. Bạn luôn có thể lấy bộ điều khiển của mình và áp dụng nó vào mẫu thứ hai;)
Liviu T.

Cảm ơn cho câu hỏi và câu trả lời này. Tôi tò mò muốn biết ý nghĩa hiệu quả của cách tiếp cận này vì vậy tôi đã thực hiện plunkr này: plnkr.co/edit/T5aZO3s5DzSnbrLELveG Tôi nhận thấy rằng mỗi khi tôi chọn một trong các mục, isSelected được gọi 6 lần (hai lần cho mỗi mục lặp lại). Bất kỳ ý tưởng tại sao điều này xảy ra hai lần cho mỗi lần? Có ai lo lắng về việc ném hơn 100 mục lặp lại trên trang và chạy nó trên thiết bị di động không? Có lẽ không phải là một vấn đề ...
Aaronius

@Aaronius Nếu bạn thêm một điểm ngắt trong hàm isSelected và làm mới, bạn sẽ thấy rằng nó được gọi trước khi nội dung của chỉ thị được phân tích cú pháp và thực thi. Tôi nghĩ vì đó là một chỉ thị thay thế tất cả các hàm bị ràng buộc được gọi hai lần
Liviu T.

Có phương pháp nào để biết chỉ các hộp kiểm đã chọn không?
Sana Joseph

35

Tôi thích sử dụng ngModelngChange chỉ khi đối phó với hộp kiểm . ngModel cho phép bạn liên kết trạng thái đã chọn / không chọn của hộp kiểm với một thuộc tính trên thực thể:

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

Bất cứ khi nào người dùng chọn hoặc bỏ chọn hộp kiểm, entity.isCheckedgiá trị cũng sẽ thay đổi.

Nếu đây là tất cả những gì bạn cần thì bạn thậm chí không cần chỉ thị ngClick hoặc ngChange. Vì bạn có hộp kiểm "Kiểm tra tất cả", rõ ràng bạn cần phải làm nhiều việc hơn là chỉ đặt giá trị của thuộc tính khi ai đó kiểm tra hộp kiểm.

Khi sử dụng ngModel với hộp kiểm, tốt nhất bạn nên sử dụng ngChange thay vì ngClick để xử lý các sự kiện đã chọn và bỏ chọn. ngChange được tạo ra chỉ dành cho loại kịch bản này. Nó sử dụng ngModelController để ràng buộc dữ liệu (nó thêm một bộ lắng nghe vào $viewChangeListenersmảng của ngModelController . Các bộ nghe trong mảng này được gọi sau khi giá trị mô hình đã được đặt, tránh được vấn đề này ).

<input type="checkbox" ng-model="entity.isChecked" ng-change="selectEntity()">

... và trong bộ điều khiển ...

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;
};

Tương tự, hộp kiểm "Kiểm tra Tất cả" trong tiêu đề:

<th>
    <input type="checkbox" ng-model="model.allItemsSelected" ng-change="selectAll()">
</th>

... và ...

// 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

Cách tốt nhất để ... thêm một lớp CSS vào <tr>thực thể chứa để phản ánh trạng thái đã chọn của nó là gì?

Nếu bạn sử dụng phương pháp tiếp cận ngModel cho ràng buộc dữ liệu, tất cả những gì bạn cần làm là thêm chỉ thị ngClass vào <tr>phần tử để thêm hoặc xóa động lớp bất cứ khi nào thuộc tính thực thể thay đổi:

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

Xem toàn bộ Plunker tại đây .


cờ allItemsSelected được đặt thành false ngay từ đầu, sau đó làm thế nào nó được đặt thành true khi nhấp vào hộp kiểm chọn Tất cả. bạn có thể vui lòng giải thích?
user2514925

11

Câu trả lời của Liviu vô cùng hữu ích đối với tôi. Hy vọng đây không phải là hình thức xấu nhưng tôi đã thực hiện một trò chơi có thể giúp đỡ người khác trong tương lai.

Hai phần quan trọng cần thiết là:

    $scope.entities = [{
    "title": "foo",
    "id": 1
}, {
    "title": "bar",
    "id": 2
}, {
    "title": "baz",
    "id": 3
}];
$scope.selected = [];

1
Angular docs có một câu trả lời đơn giản hơn cho phần kiểm tra tất cả. docs.angularjs.org/api/ng.directive:ngĐã kiểm tra . Thu thập những gì được kiểm tra là điều tôi đang cố gắng tìm ra.
Hayden,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.