우리가 ngRepeat 디렉티브를 이용해 todos 배열 데이터를 출력해 봤다. 이번에는 출력된 리스트를 필터링해 보겠다.

필터링할 수 있는 기준은 아래와 같다.

  • completed: 완료된 투두 리스트
  • active: 미완료된 투두 리스트
  • all: 모든 투두 리스트

ngRepeat는 기본적으로 필터기능을 지원한다. 완료된 투두 리스트만 필터링 한다고 하면 아래와 같은 코드를 작성하면 된다.

<ul ng-repeat="todo in todos | filter: {completed: true}">

반대로 미완료된 투두만 필터링 할경우에는 filter: {completed: false}로 할것이다. 모든 투두 리스트를 보여줄 경우 즉 필터링하지 않을 경우에는 filter: {}처럼 빈 객체를 필터에 추가하면 된다.

자 다시 생각해 보자. 필터기능을 어떻게 UI랑 함께 구현할 수 있을까?

필터 버튼 만들기

completed, active, all 세개를 버튼을 만들어서 각각 클릭할때 마다 필터링 로직이 동작하도록 하자.

<div class="btn-group" role="group" aria-label="...">
  <button type="button" ng-click="status='completed'">Completed</button>
  <button type="button" ng-click="status='active'">Active</button>
  <button type="button" ng-click="status=''">All</button>
</div>

상태정보: $scope.status

필터에 관련된 정보를 스코프변수 $scope.status에 저장한다. 버튼을 클릭할 때마다 이 값은 변경될 것이다. 왜냐하면 각 필터버튼에 ng-click 핸들러를 추가했기 때문이다.

<button ng-click="status='completed'">

ng-click은 함수뿐만이 아니라 자바스크립트 명령문도 올 수 있다.

필터정보: $scope.statusFilter

ng-repeat을 사용할때 filter에 적당한 필터 정보를 설정해야 하는데… 이게 동적으로 동작해야한다. 그럼 필터값에 변수를 할당하면 어떨까?

<ul ng-repeat="todo in todos | filter:statusFilter">

사용자가 어던 필터 버튼을 클릭하느냐에 따라 필터에 설정된 변수 statusFilter의 값이 변경될 것이다.

$watch

앵귤러에서 가장 많이 사용하지만 남용되어서는 안될 함수가 있는데 바로 $watch 함수다. 이것은 스코프 변수의 변경을 감지하고 그때마다 사용자가 설정한 함수를 실행한다. 물론 많이 사용하면 메모리 자원도 그만큼 많이 사용하 때문에 이점을 조심해야 한다.

우리는 $watch변수를 통해 버튼클릭으로 변경되 status 변수를 감시하고 그 값에 따라 statusFilter 값을 변경해 주면 필터가 동작할 것이다.

TodomvcCtrl.js:

// 필터버튼을 클릭하고 status 값이 변경되면 $watch()에 등록한 함수가 동작한다.
$scope.$watch('status', function () {
  if ($scope.status === 'completed') {        // Completed 클릭시
    $scope.statusFilter = {completed: true};  // 필터를 설정한다.
  } else if ($scope.status === 'active') {    // Active 클릭시
    $scope.statusFilter = {completed: false}; // 필터를 설정한다.
  } else {                                    // All 클릭시
    $scope.statusFilter = {};                 // 필터를 해제한다.
  }
})

화면을 다시 리로딩하여 필터가 동작하는 것을 확인해 보자.

Clear All 버튼 추가하기

거의 다왔다. 화면구성 마지막 단계다. 버튼을 하나만 하나만 더 추가하자. 투두를 하나씩 완료하면 완료된 투두 목록이 많아질 것이기 때문에 “Clear Completed” 버튼을 만들어야겠다.

사실 이 부분은 별로 설명하지 않아도 잘 따라온 독자라면 혼자서도 구현할수 있어야 한다. 총 정리하는 차원에 전체 코드를 실어놓겠다.

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, initial-scale=1, user-scalable=yes">
  <title>Angular | TodoMVC</title>
  <link rel="stylesheet"
        href="node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body ng-app="todomvc">
<div ng-controller="TodomvcCtrl" class="container">
  <h1>Todos
    <small> {{status}}</small>
  </h1>

  <ul class="list-unstyled">
    <li>
      <form ng-submit="addTodo(newTodo)">
        <div class="input-group">
          <input type="text" ng-model="newTodo" class="form-control"
                 placeholder="Type todos" autofocus>
          <span class="input-group-btn">
            <button class="btn btn-success" type="submit">Add</button>
          </span>
        </div>
      </form>
    </li>
  </ul>

  <ul ng-repeat="todo in todos | filter:statusFilter" class="list-unstyled">
    <li class="todo-item">
      <div class="input-group">
        <span class="input-group-addon">
          <input type="checkbox" aria-label="..." ng-model="todo.completed">
        </span>
        <input type="text" class="form-control" aria-label="..." ng-model="todo.title">
        <div class="input-group-btn">
          <button class="btn btn-danger" ng-click="remove(todo.id)">Remove</button>
        </div>
      </div>
    </li>
  </ul>

  <div class="btn-group" role="group" aria-label="...">
    <button type="button" class="btn btn-default" ng-click="status='completed'">
      Completed
    </button>
    <button type="button" class="btn btn-default" ng-click="status='active'">
      Active
    </button>
    <button type="button" class="btn btn-default" ng-click="status=''">All
    </button>
    <button type="button" class="btn btn-default" ng-click="clearCompleted()">
      Clear Completed
    </button>
  </div>

</div>

<script src="node_modules/angular/angular.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers/TodomvcCtrl.js"></script>

</body>
</html>

js/app.js:

angular.module('todomvc',[]);

js/controllers/TodomvcCtrl.js:

angular.module('todomvc')
    .controller('TodomvcCtrl', function ($scope) {
      $scope.todos = [{
        id: 1,
        title: '요가 수행하기',
        completed: false
      }, {
        id: 2,
        title: '어머니 용돈 드리기',
        completed: true
      }];

      $scope.addTodo = function (todoTitle) {
        todoTitle = todoTitle.trim();
        if (!todoTitle) return;

        var newId = !$scope.todos.length ?
            1 : $scope.todos[$scope.todos.length - 1].id + 1;
        var newTodo = {
          id: newId,
          title: todoTitle,
          completed: false
        };

        $scope.todos.push(newTodo);
      };

      $scope.remove = function (id) {
        if (!id) return;

        var deleltedTodoIdx = $scope.todos.findIndex(function (todo) {
          return todo.id === id;
        });

        if (deleltedTodoIdx === -1) return;

        $scope.todos.splice(deleltedTodoIdx, 1);
      }

      $scope.$watch('status', function () {
        if ($scope.status === 'completed') {
          $scope.statusFilter = {completed: true}
        } else if ($scope.status === 'active') {
          $scope.statusFilter = {completed: false}
        } else {
          $scope.statusFilter = {}
        }
      });

      $scope.clearCompleted = function () {
        var incompleteTodos = $scope.todos.filter(function (todo) {
          return !todo.completed;
        });
        angular.copy(incompleteTodos, $scope.todos);
      }

    });

관련글:

  1. [앵귤러로 Todo앱 만들기] 노드 설치
  2. [앵귤러로 Todo앱 만들기] 앵귤러 로딩
  3. [앵귤러로 Todo앱 만들기] 컨트롤러
  4. [앵귤러로 Todo앱 만들기] 투두 목록 출력하기
  5. [앵귤러로 Todo앱 만들기] 새로운 투두 추가하기
  6. [앵귤러로 Todo앱 만들기] Bootstrap으로 꾸미기
  7. [앵귤러로 Todo앱 만들기] 투두 목록 필터링
  8. [앵귤러로 Todo앱 만들기] Directive
  9. [앵귤러로 Todo앱 만들기] Service
  10. [앵귤러로 Todo앱 만들기] Express.js로 웹서버 만들기
  11. [앵귤러로 Todo앱 만들기] Static File
  12. [앵귤러로 Todo앱 만들기] APIs
  13. [앵귤러로 Todo앱 만들기] $http로 앵귤러 서비스 개선하기