80

次のテンプレートのエンティティのコレクションをレンダリングするAngularJSディレクティブがあります。

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

ご覧のとおり、<table>ここでは、各行を独自のチェックボックスで個別に選択するか、にあるマスターチェックボックスを使用してすべての行を一度に選択できます<thead>。かなり古典的なUI。

次の最善の方法は何ですか。

  • 単一の行を選択します(つまり、チェックボックスがオンになっている場合、選択したエンティティのIDを内部配列に追加<tr>し、選択した状態を反映するために、エンティティを含むCSSクラスを追加します)?
  • すべての行を一度に選択しますか?(つまり、のすべての行に対して前述のアクションを実行します<table>

私の現在の実装は、カスタムコントローラーをディレクティブに追加することです。

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

より具体的には、私は疑問に思います:

  • 上記のコードはコントローラーに属しているのですか、linkそれとも関数に含める必要がありますか?
  • jQueryが必ずしも存在するわけではない(AngularJSはそれを必要としない)とすると、DOMトラバーサルを行うための最良の方法は何ですか?jQueryがない<tr>と、特定のチェックボックスの親を選択したり、テンプレート内のすべてのチェックボックスを選択したりするのに苦労しています。
  • に渡す$eventことupdateSelection()はあまりエレガントではないようです。クリックされたばかりの要素の状態(チェックされている/チェックされていない)を取得するためのより良い方法はありませんか?

ありがとうございました。

4

3 に答える 3

123

これは私がこの種のことをしている方法です。Angularは、命令型ではなく、宣言型のdomの操作を好む傾向があります(少なくとも、それは私がそれで遊んでいる方法です)。

マークアップ

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

そしてコントローラーで

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

編集getSelectedClass()エンティティ全体を想定していますが、エンティティのIDのみで呼び出されていましたが、現在は修正されています

于 2012-08-08T21:06:15.513 に答える
35

チェックボックスを処理するときは、 ngModelおよびngChangeディレクティブを使用することを好みます。ngModelを使用すると、チェックボックスのオン/オフの状態をエンティティのプロパティにバインドできます。

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

ユーザーがチェックボックスをオンまたはオフにするたびに、entity.isChecked値も変更されます。

これが必要なすべてである場合は、ngClickまたはngChangeディレクティブも必要ありません。[すべてチェック]チェックボックスがあるので、誰かがチェックボックスをオンにしたときにプロパティの値を設定する以上のことを行う必要があることは明らかです。

チェックボックスを指定してngModelを使用する場合は、チェックされているイベントとチェックされていないイベントを処理するために、ngClickではなくngChangeを使用することをお勧めします。ngChangeは、この種のシナリオに対してのみ行われます。データバインディングにngModelControllerを使用します(ngModelControllerの$viewChangeListeners配列にリスナーを追加します。この配列のリスナーは、モデル値が設定された後に呼び出され、この問題を回避します)。

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

...そしてコントローラーで..。

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

同様に、ヘッダーの[すべてチェック]チェックボックスは次のとおりです。

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

... と ...

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

<tr>選択した状態を反映するために、エンティティを含むCSSクラスを追加する最良の方法は何ですか?

データバインディングにngModelアプローチを使用する場合、必要なのは、要素にngClassディレクティブを追加し<tr>て、エンティティプロパティが変更されるたびにクラスを動的に追加または削除することだけです。

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

ここで完全なプランカーを参照してください。

于 2014-03-24T13:33:29.403 に答える
11

Liviuの答えは私にとって非常に役に立ちました。これが悪い形ではないことを願っていますが、私は将来誰かを助けるかもしれないフィドルを作りました。

必要な2つの重要な部分は次のとおりです。

    $scope.entities = [{
    "title": "foo",
    "id": 1
}, {
    "title": "bar",
    "id": 2
}, {
    "title": "baz",
    "id": 3
}];
$scope.selected = [];
于 2013-02-13T22:15:42.503 に答える