AngularJS 지시문의 확인란 클릭에 응답하는 방법은 무엇입니까?


79

다음 템플릿에서 엔티티 컬렉션을 렌더링 하는 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.

다음을 수행하는 가장 좋은 방법은 무엇입니까?

  • 단일 행을 선택 <tr>합니까 (예 : 확인란을 선택한 경우 선택한 엔티티의 ID를 내부 배열에 추가 하고 엔티티를 포함 하는에 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()것은 그다지 우아하지 않은 것 같습니다. 방금 클릭 한 요소의 상태 (선택 / 선택 해제)를 검색하는 더 좋은 방법이 없습니까?

감사합니다.

답변:


122

이것이 제가 이런 일을 해왔 던 방식입니다. 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로만 호출되었으며 이제 수정되었습니다.


고마워, Liviu! 작동하고 도움이되었습니다. 그리고 당신 덕분에 나는 ngChecked지시 에 대해 배웠습니다 . (내 유일한 후회는 우리가 자세한 조금 덜이 코드를 만들 수 없다는 것입니다.)
AngularChef

1
장황하게 생각하지 말고 우려의 분리 측면에서 생각하십시오. 데이터 모델은 데이터가 표시되는 방식을 알면 안됩니다. 컨트롤러에는 tr 또는 td에 대한 언급이 없음을 기억하십시오. 기껏해야 확인란이 포함되어 있지만 제거 할 수도 있습니다. 당신은 항상 당신의 컨트롤러를 가지고 두 번째 템플릿에 적용 할 수 있습니다)
비우 T.에게

이 질문과 답변에 감사드립니다. 이 접근 방식이 효율성에 미치는 영향을 알고 싶었 기 때문에 다음과 같이 plunkr를 만들었습니다. plnkr.co/edit/T5aZO3s5DzSnbrLELveG 항목 중 하나를 선택할 때마다 isSelected가 6 번 호출됩니다 (각 반복기 항목에 대해 두 번). 왜 이것이 각각 두 번 발생하는지 아십니까? 페이지에 100 개 이상의 리피터 항목을 표시하고 모바일에서 실행하는 것에 대해 우려하는 사람이 있습니까? 아마 ... 문제가되지 않습니다
Aaronius

@Aaronius isSelected 함수에 중단 점을 추가하고 새로 고치면 지시문의 내용이 구문 분석되고 실행되기 전에 호출되는 것을 볼 수 있습니다. 나는 두 번 호출되는 모든 바인딩 기능을 대체 않는 지침이기 때문에 생각
비우 T.

선택한 체크 박스 만 알 수있는 방법이 있습니까?
Sana Joseph

35

확인란을 다룰ngModelngChange 지시문 을 사용하는 것을 선호합니다 . 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}">

여기 에서 전체 Plunker를 참조 하십시오 .


allItemsSelected 플래그는 처음에 false로 설정되고 모두 선택 확인란을 클릭 할 때 true로 설정되는 방식입니다. 설명해 주시겠습니까?
user2514925

11

Liviu의 답변은 저에게 매우 도움이되었습니다. 이것이 나쁜 형태는 아니지만 미래에 다른 사람을 도울 수 있는 바이올린 을 만들었습니다 .

필요한 두 가지 중요한 부분은 다음과 같습니다.

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

1
Angular 문서에는 모든 부분 확인에 대한 더 간단한 대답이 있습니다. docs.angularjs.org/api/ng.directive:ngChecked . 확인 된 것을 수집하는 것은 제가 알아 내려고하는 것입니다.
Hayden
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.