Статьи

AngularJS: Различные способы использования Array Filters

AngularJS предоставляет функцию фильтрации, которую можно использовать для форматирования входного значения или для фильтрации массива с заданными совпадающими критериями ввода. Например, вы можете использовать фильтр ‘date’ для форматирования значения Date в удобочитаемое представление Date, например MM-DD-YYYY, как {{dob | дата}} .

С другой стороны, есть функция фильтрации массива, которая очень полезна при фильтрации данных из массива объектов JavaScript. Фильтрация массива очень часто используется вместе с таблицей
директива ng-repeat .

Например, у нас может быть список Todos, которые мы можем отобразить в таблице, используя тег ng-repeat . И у нас может быть текстовое поле для поиска задач, которое соответствует любому из свойств данных объекта Todo следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$scope.todos = [
    {id: 1,title: 'Learn AngularJS', description: 'Learn AngularJS', done: true, date: new Date()}  ,
    {id: 2,title: 'Explore ui-router', description: 'Explore and use ui-router instead of ngRoute', done: true, date: new Date()}  ,
    {id: 3,title: 'Play with Restangular', description: 'Restangular seems better than $resource, have a look', done: false, date: new Date()}  ,
    {id: 4,title: 'Try yeoman', description: 'No more labour work..use Yeoman', done: false, date: new Date()}  ,
    {id: 5,title: 'Try MEANJS', description: 'Aah..MEANJS stack seems cool..why dont u try once', done: false, date: new Date()}               
            ];
 
  
<input type="text" ng-model="searchTodos">
 <table class="table table-striped table-bordered">
 <thead>
  <tr>
   <th>#</th>
   <th>Title</th>
   <th>Description</th>
   <th>Done?</th>
   <th>Date</th>
  </tr>
 </thead>
 <tbody>
  <tr ng-repeat="todo in todos| filter: searchTodos">
   <td>{{$index + 1}}</td>
   <td>{{todo.title}}</td>
   <td>{{todo.description}}</td>
   <td>{{todo.done}}</td>
   <td>{{todo.date | date}}</td>
  </tr>
 
 </tbody>
 
</table>

Обратите внимание, что атрибут ng-model нашего поля ввода поиска имеет значение searchTodos , которое мы использовали для фильтрации по атрибуту ng-repeat . По мере ввода в поле ввода поиска массив $ scope.todos будет фильтроваться, и будут отображаться только соответствующие записи. Это фильтр типа « сопоставить с чем угодно », что означает, что критерии поиска будут проверяться по всем свойствам ( идентификатор, заголовок, описание, дата ) объекта Todo.

Если вы хотите искать только в одном поле, скажем « описание », вы можете применить фильтр следующим образом:

1
<tr ng-repeat="todo in todos| filter: {description: searchTodos}">

Если вы хотите отобразить только Todos, которые еще не сделаны, вы можете сделать это следующим образом:

1
<tr ng-repeat="todo in todos| filter: {description: searchTodos, done: false}">

Обратите внимание, что здесь 2 условия будут применяться с использованием условий AND.

Если вы хотите отобразить только Todos, которые еще не сделаны, и вы хотите искать по всем полям, а не только по « описанию », то вы можете сделать это следующим образом:

1
<tr ng-repeat="todo in todos| filter: {$: searchTodos, done: false}">

Здесь $ означает все поля. Пока все хорошо, так как это простой и понятный случай.

Как насчет наличия вложенных объектов в наших объектах Array, и мы хотим осуществлять поиск на основе свойства вложенного объекта?

Давайте посмотрим на такой тип сценария. Чтобы объяснить эти сценарии, я использую несколько примеров кода из моего приложения ebuddy.

В моем приложении ebuddy у меня есть модуль ExpenseManager, где я буду отслеживать свои расходы следующим образом:

  • У меня будет список Счетов, таких как Наличные, Сберегательный счет, Кредитная карта и т. Д. С данными текущего баланса.
  • У меня будет список получателей платежей, таких как HouseRent, PowerBill, Salary и т. Д., Которые попадают в категории ДОХОДОВ или РАСХОДОВ.
  • Я запишу все свои транзакции, выбрав один из аккаунтов, получателя и сумму.

Это приложение просто для записи моих финансовых транзакций, чтобы я мог видеть ежемесячные отчеты по счету или получателю. Я надеюсь, что вы получите представление о модели предметной области.

Теперь давайте создадим простое приложение AngularJS и установим несколько примеров данных.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
 <html ng-app="myApp">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>My AngularJS App</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/js/bootstrap.min.js"></script>
   
  <script>
      var myApp = angular.module('myApp',[]);
      myApp.controller('SampleController', function($scope){
            $scope.accounts = [
                    {id: 1, name: 'Cash'},
                    {id: 2, name: 'Bank Savings'}
                  ];
            $scope.payees = [
                    {id:'1',name:'HouseRent', txnType:'EXPENDITURE'},
                    {id: '2', name:'InternetBill', txnType:'EXPENDITURE'},
                    {id:'3', name: 'PowerBill', txnType:'EXPENDITURE'},
                    {id:'4', name: 'Salary', txnType:'INCOME'}
                  ];
            $scope.transactions = [
                {id:'1', txnType:'EXPENDITURE', amount: 1000, account: $scope.accounts[0], payee: $scope.payees[0]},
                {id:'2', txnType:'EXPENDITURE', amount: 500, account: $scope.accounts[1], payee: $scope.payees[1]},
                {id:'3', txnType:'EXPENDITURE', amount: 1200, account: $scope.accounts[0], payee: $scope.payees[1]},
                {id:'4', txnType:'INCOME', amount: 5000, account: $scope.accounts[1], payee: $scope.payees[3]},
                {id:'5', txnType:'EXPENDITURE', amount:200, account: $scope.accounts[0], payee: $scope.payees[2]}
            ];
             
      });
       
  </script
</head>
<body ng-controller="SampleController">
<br/>
 <div class="col-md-8 col-md-offset-2">
         
    <h3>Transaction Details</h3>
    <table class="table table-striped table-bordered">
        <thead>
            <tr>
                <th>#</th>
                <th>Account</th>
                <th>Type</th>
                <th>Payee</th>
                <th>Amount</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="txn in transactions">
                <td>{{$index + 1}}</td>
                <td>{{txn.account.name}}</td>
                <td>{{txn.txnType}}</td>
                <td>{{txn.payee.name}}</td>
                <td>{{txn.amount}}</td>
            </tr>
        </tbody>
    </table>
</div>
 
</body>
</html>

Это очень простая страница AngularJS, которая отображает список транзакций в таблице. Обратите внимание, что транзакции содержат вложенные объекты ( account , payee ), и мы отображаем вложенные свойства ( txn.account.name , txn.payee.name ) в нашей таблице.

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

Дело № 1: Поиск по имени получателя

В нашем объекте транзакции у нас есть вложенный объект получателя, который содержит свойство name, по которому мы хотим выполнить поиск.

Давайте создадим форму, которая будет содержать все наши фильтры перед таблицей транзакций.

Первая мысль, которая пришла мне в голову, чтобы выполнить поиск по вложенному свойству, это использовать путь вложенного свойства в фильтре следующим образом:

1
2
3
<input type="text" ng-model="payeeName">
...
<tr ng-repeat="txn in transactions| filter: {payee.name : payeeName}">

Но ЭТО НЕ БУДЕТ РАБОТАТЬ.

Для поиска по вложенному свойству мы можем назвать наше поле ввода ng-model в соответствии с целевым путем свойства и использовать имя корневого объекта в качестве фильтра следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<div class="col-md-8 col-md-offset-2">
     <form class="form-horizontal" role="form">
        <div class="form-group">
          <label for="input1" class="col-sm-4 control-label">Search by Payee</label>
          <div class="col-sm-6">
            <input type="text" class="form-control" id="input1" placeholder="Payee Name" ng-model="filterTxn.payee.name">
          </div>
        </div>
  <!-- additional filters will come here -->
 </form>
 <h3>Transaction Details</h3>
 <table class="table table-striped table-bordered">
  ...
  <tbody>
            <tr ng-repeat="txn in transactions| filter: filterTxn">
                ...
    ...
            </tr>
  </tbody>
 </table>
</div>

Заметьте, что мы связали поле ввода ng-model с « filterTxn.payee.name » и использовали filter: filterTxn в качестве фильтра. Таким образом, txn.payee.name будет сопоставлено с filterTxn.payee.name .

Случай № 2: Фильтр по выпадающим спискам

Мы хотели бы отфильтровать транзакции с помощью раскрывающегося списка «Счета». Сначала нам нужно заполнить выпадающий список с помощью $ scope.accounts и использовать его в качестве фильтра.

Добавьте следующий фильтр после нашего первого фильтра.

1
2
3
4
5
6
7
8
9
<div class="form-group">
  <label for="input2" class="col-sm-4 control-label">Search By Account</label>
  <div class="col-sm-6">
  <select id="input2" class="form-control" ng-model="filterTxn.account">
   <option value="">All Accounts</option>
   <option ng-repeat="item in accounts" value="{{item.id}}">{{item.name}}</option>
  </select>
  </div>
</div>

Здесь мы заполняем поле <select> массивом $ scope.accounts , отображая имя учетной записи и используя id в качестве значения.

Ключевой частью здесь является то, что мы привязали ng-модель к filterTxn.account . Когда мы выбираем учетную запись, ссылка на выбранный объект учетной записи будет сохранена в файле filterTxn.account . Поскольку у нас уже есть filterTxn в качестве фильтра, фильтр учетной записи также будет применяться вместе с фильтром имени получателя.

Также обратите внимание, что первый параметр «Все учетные записи» является пустым ( «» ), и AngularJS будет рассматривать его как нулевой , поэтому при выборе параметра «Все учетные записи» фильтр учетных записей применяться не будет.

Дело № 3: Поиск по типу транзакции

Мы хотим отфильтровать транзакцию по типу транзакции ( ДОХОД или РАСХОДЫ ):

Добавьте следующий фильтр после второго фильтра:

01
02
03
04
05
06
07
08
09
10
<div class="form-group">
  <label for="input3" class="col-sm-4 control-label">Search By Type</label>
  <div class="col-sm-6">
  <select id="input3" class="form-control" ng-model="filterTxn.txnType">
   <option value="">All Types</option>
   <option value="EXPENDITURE">EXPENDITURE</option>
   <option value="INCOME">INCOME</option>
  </select>
  </div>
</div>

Я надеюсь, что дальнейшее объяснение не нужно для этого!

Дело № 4: Поиск по получателям по типу расходов

Ааа .. это интересно! Мы хотим осуществлять поиск по именам получателей, но только в получателях типа РАСХОДЫ .

Мы не можем просто применить фильтр как «filter: expPayeeFilter | filter: {txnType: ‘EXPENDITURE’} ”, потому что он всегда будет фильтроваться по EXPENDITURE .

Поэтому мы создадим пользовательский фильтр для выполнения « поиска по имени получателя в получателях типа EXPENDITURE только при вводе некоторого текста фильтра » следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
myApp.filter('expenditurePayeeFilter', [function($filter) {
 return function(inputArray, searchCriteria, txnType){        
  if(!angular.isDefined(searchCriteria) || searchCriteria == ''){
   return inputArray;
  }        
  var data=[];
  angular.forEach(inputArray, function(item){            
   if(item.txnType == txnType){
    if(item.payee.name.toLowerCase().indexOf(searchCriteria.toLowerCase()) != -1){
     data.push(item);
    }
   }
  });     
  return data;
 };
}]);

Мы создали собственный фильтр, используя myApp.filter (), и внутри него мы использовали angular.forEach () для итерации по входному массиву, остальные просто javascript..no волшебство.

Теперь мы применим этот пользовательский фильтр следующим образом:

1
2
3
4
5
6
7
<tr ng-repeat="txn in transactions| filter: filterTxn | expenditurePayeeFilter:searchCriteria:'EXPENDITURE'">
 <td>{{$index + 1}}</td>
 <td>{{txn.account.name}}</td>
 <td>{{txn.txnType}}</td>
 <td>{{txn.payee.name}}</td>
 <td>{{txn.amount}}</td>
</tr>

Наблюдайте за синтаксисом: customFilterName: param1: param2: ..: paramN .

Эти параметры будут переданы в качестве аргументов функции внутри нашей пользовательской директивы.

Мы видели несколько интересных вариантов использования функций фильтрации массивов AngularJS.

Надеюсь, поможет!