AngularJS

Search for '{{search_term}}'

AngularJS: фильтрация таблиц с ng-repeat

CMOS is the Code-Maven Open Source podcast that also includes video interviews. Subscribe to this feed RSS feed with your Podcast listener app or via iTunes iTunes.

Создание таблицы с помощью ng-repeat весьма простое. Добавление поискового блока для фильтрации результатов тоже простое, но только если вы хотите использовать простой поиск по тексту. Немного сложнее, если вы хотите искать по значениям меньше, чем введенное пользователем.

В нашем примере мы будем использовать планеты Солнечной Системы. У нас есть имя, средняя дистанция от Солнца в единицах "дистанция от Земли до Солнца" и масса относительно массы Земли. Таким образом, оба значения равны 1 для Земли. (Земля это не центр системы, если что :).

В первом примере мы сделали таблицу из данных, прописанных в коде, и добавили текстовый фильтр на все поля:

examples/angular/angular_table_filter_1.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>Table Filter</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
  <script>
  angular.module('TableFilterApp', [])
    .controller('TableFilterController', function($scope) {
      $scope.planets = [
        {
          name : 'Mercury',
          distance : 0.4,
          mass : 0.055
        },
        {
          name : 'Venus',
          distance : 0.7,
          mass : 0.815
        },
        {
          name : 'Earth',
          distance: 1,
          mass : 1
        },
        {
          name : 'Mars',
          distance : 1.5,
          mass : 0.107
        },
        {
          name : 'Ceres',
          distance : 2.77,
          mass :     0.00015
        },
        {
          name : 'Jupiter',
          distance : 5.2,
          mass :   318
        },
        {
          name : 'Saturn',
          distance : 9.5,
          mass :    95
        },
        {
          name : 'Uranus',
          distance : 19.6,
          mass :   14
        },
        {
          name : 'Neptune',
          distance : 30,
          mass : 17
        },
        {
          name : 'Pluto',
          distance : 39,
          mass : 0.00218
        },
        {
          name : 'Charon',
          distance : 39,
          mass :  0.000254
        }
      ];
    
    });
  </script>

</head>
<body ng-app="TableFilterApp" ng-controller="TableFilterController">

<table>
<tr><th>Name</th><th>Average Distance</th><th>Mass</th></tr>
<tr><td><input ng-model="f.name"></td><td><input ng-model="f.distance"></td></td><td><input ng-model="f.mass"></td></tr>
<tr ng-repeat="p in planets | filter:f"><td>{{p.name}}</td><td>{{p.distance}}</td><td>{{p.mass}}</td></tr>
</table>
</body>
</html>


Try!

Интересная часть кода кроется в этих двух строках:

examples/angular/angular_table_filter_1.js

<tr><td><input ng-model="f.name"></td><td><input ng-model="f.distance"></td></td><td><input ng-model="f.mass"></td></tr>
<tr ng-repeat="p in planets | filter:f"><td>{{p.name}}</td><td>{{p.distance}}</td><td>{{p.mass}}</td></tr>

Вторая строка это то, что строит таблицу. Это обычная ng-repeat строка, но результаты фильтруются на основе содержимого объекта f. Атрибуты этого объекта связаны в полем ввода в первой строке. Каждый атрибут будет фильтроваться в соответствии с полем исходного массива.

Хотя в нашем случае фильтр имеет смысл только для первой колонки. Так как это простой текст-фильтр, то как-то бессмысленно искать все планеты, у которых значение дистанции содержит 7. В других таблицах, вероятно, это интереснее.

Поиск значений больше или меньше

Гораздо интереснее найти все планеты, дистанция до которых меньше 2. Или где масса меньше 20.

examples/angular/angular_table_filter_2.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>Table Filter</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
  <script>
  angular.module('TableFilterApp', [])
    .controller('TableFilterController', function($scope) {
      $scope.planets = [
        {
          name : 'Mercury',
          distance : 0.4,
          mass : 0.055
        },
        {
          name : 'Venus',
          distance : 0.7,
          mass : 0.815
        },
        {
          name : 'Earth',
          distance: 1,
          mass : 1
        },
        {
          name : 'Mars',
          distance : 1.5,
          mass : 0.107
        },
        {
          name : 'Ceres',
          distance : 2.77,
          mass :     0.00015
        },
        {
          name : 'Jupiter',
          distance : 5.2,
          mass :   318
        },
        {
          name : 'Saturn',
          distance : 9.5,
          mass :    95
        },
        {
          name : 'Uranus',
          distance : 19.6,
          mass :   14
        },
        {
          name : 'Neptune',
          distance : 30,
          mass : 17
        },
        {
          name : 'Pluto',
          distance : 39,
          mass : 0.00218
        },
        {
          name : 'Charon',
          distance : 39,
          mass :  0.000254
        }
      ];
      $scope.f = {};

      $scope.filter_by = function(field) {
        console.log(field);
        console.log($scope.g[field]);
        if ($scope.g[field] === '') {
             delete $scope.f['__' + field];
             return;
        }
        $scope.f['__' + field] = true;
        $scope.planets.forEach(function(v) { v['__' + field] = v[field] < $scope.g[field]; })
      }
    
    });
  </script>

</head>
<body ng-app="TableFilterApp" ng-controller="TableFilterController">

<table>
<tr><th>Name</th><th>Average Distance</th><th>Mass</th></tr>
<tr><td><input ng-model="f.name"></td><td><input ng-model="g.distance" ng-change="filter_by('distance')"></td><td><input ng-model="g.mass" ng-change="filter_by('mass')"></td></tr>
<tr ng-repeat="p in planets | filter:f"><td>{{p.name}}</td><td>{{p.distance}}</td><td>{{p.mass}}</td></tr>
</table>
</body>
</html>


Try!

Это сильно сложнее. Я пока не смог найти решения лучше, так что сейчас каждый раз, когда пользователь вводит значение либо в поле "distance", либо в поле "mass", код будет проходить по всем значениям массива и добавлять дополнительный ключ к каждому объекту со значениями true, если объект подходит под условие, или false, если не подходит. Дополнительный ключ сделан с помощью добавления двух подчеркиваний '__' перед именем исходного ключа. Предполагается, что мы вряд ли столкнемся с данными, где будут такие ключи.

HTML-код поменялся незначительно. Вместо привязки поля ввода к атрибуту объекта фильтра, мы привяжем его к другому объекту (с именем g), а также добавим вызов функции, используя ng-change.

examples/angular/angular_table_filter_2b.js

<tr><td><input ng-model="f.name"></td><td><input ng-model="g.distance" ng-change="filter_by('distance')"></td><td><input ng-model="g.mass" ng-change="filter_by('mass')"></td></tr>

Когда пользователь поменяет значение в одном из двух полей, Angular вызовет функцию filter_by. Для начала мы проверим, не пусто ли поле ввода. Если пусто, то удалим все наши созданные ключи.

Если в поле будет введено значение, мы пройдемся по всем объектам в массиве planets и добавим соответствующий атрибут со значением true или false. Реальная фильтрация уже будет произведена AngularJS.

examples/angular/angular_table_filter_2.js

      $scope.f = {};

      $scope.filter_by = function(field) {
        if ($scope.g[field] === '') {
             delete $scope.f['__' + field];
             return;
        }
        $scope.f['__' + field] = true;
        $scope.planets.forEach(function(v) { v['__' + field] = v[field] < $scope.g[field]; })
      }
 

Savenkova Natalya
Переводчик
Savenkova Natalya
Gabor Szabo
Автор
Gabor Szabo

Comments