Как отобразить длину отфильтрованных данных ng-repeat

438

У меня есть массив данных, который содержит много объектов (формат JSON). Следующее может быть принято в качестве содержимого этого массива:

var data = [
  {
    "name": "Jim",
    "age" : 25
  },
  {
    "name": "Jerry",
    "age": 27
  }
];

Теперь я отображаю эти детали как:

<div ng-repeat="person in data | filter: query">
</div

Здесь запрос моделируется в поле ввода, в котором пользователь может ограничить отображаемые данные.

Теперь у меня есть другое местоположение, в котором я отображаю текущее количество отображаемых людей / человек, т.е. Showing {{data.length}} Persons

Что я хочу сделать, так это то, что когда пользователь ищет человека и отображаемые данные фильтруются на основе запроса, Showing...personsтакже меняются значения людей, которые отображаются в данный момент. Но этого не происходит. В нем всегда отображается общее количество людей в данных, а не в отфильтрованном. Как узнать количество отфильтрованных данных?

user109187
источник

Ответы:

746

Для Angular 1.3+ (кредиты @Tom)

Используйте псевдоним (Docs: Angular 1.3.0: ngRepeat , прокрутите вниз до раздела Arguments ):

<div ng-repeat="person in data | filter:query as filtered">
</div>

Для угловых до 1,3

Присвойте результаты новой переменной (например filtered) и получите к ней доступ:

<div ng-repeat="person in filtered = (data | filter: query)">
</div>

Показать количество результатов:

Showing {{filtered.length}} Persons

Возьмите похожий пример . Кредиты идут на Павла Козловского

Wumms
источник
42
Это простой ответ, который не повторяет выполнение фильтра.
Агмин
3
@ Бен: Да. Вы можете получить доступ к нему внутри контроллера, используя $scope.filtered.length.
Wumms
2
Это не работает для меня. Журналы undefined, вероятно, потому что во время регистрации, переменная еще не определена. Итак, как мне убедиться, что код в контроллере запускается после того, как переменная была определена и фильтр был применен в представлении?
Бен
6
@Ben: Я думаю, это не по теме. Я бы подумал о создании пользовательского фильтра, например, jsfiddle , однако вы также можете посмотреть его внутри контроллера:$scope.$watch('filtered', function() { console.log('filtered:', $scope.filtered); });
Wumms
3
Версия 1.3+ не работает , если я хотел бы добавить limitTo: person in data | filter:query as filtered | limitTo:5. Поэтому я должен был использовать предыдущую версию 1.3:person in filtered = (data | filter: query) | limitTo: 5
benjovanic
333

Для полноты, в дополнение к предыдущим ответам (выполнить расчет видимых людей внутри контроллера), вы также можете выполнить эти вычисления в своем HTML-шаблоне, как в примере ниже.

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

<p>Number of visible people: {{(data|filter:query).length}}</p>
<p>Total number of people: {{data.length}}</p>
  • {{data.length}} - печатает общее количество людей
  • {{(data|filter:query).length}} - печатает отфильтрованное количество людей

Обратите внимание, что это решение отлично работает, если вы хотите использовать отфильтрованные данные только один раз на странице. Однако если вы используете отфильтрованные данные более одного раза, например, для представления элементов и для отображения длины отфильтрованного списка, я бы предложил использовать выражение псевдонима (описанное ниже) для AngularJS 1.3+ или решение, предложенное @Wumms для версии AngularJS до версии 1.3.

Новая функция в Angular 1.3

Создатели AngularJS также заметили эту проблему и в версии 1.3 (бета 17) они добавили выражение «псевдоним», которое будет хранить промежуточные результаты ретранслятора после применения фильтров, например

<div ng-repeat="person in data | filter:query as results">
    <!-- template ... -->
</div>

<p>Number of visible people: {{results.length}}</p>

Выражение псевдонима позволит предотвратить многократный вопрос выполнения фильтра.

Я надеюсь, что это поможет.

Том
источник
4
Означает ли это, что фильтр выполняется дважды?
Вспышка
9
Да, это выполняется дважды.
Натанкахилл,
11
убедитесь, что вы прочитали ответ @Wumms, прежде чем решите использовать его (и не будете) ...
epeleg
1
Неважно, я вижу, что ответ не содержал части о псевдониме, когда вы сделали свой комментарий.
Адам Гудвин
2
Этот ответ поддерживает несколько выражений фильтра в отличие от текущего верхнего ответа. т.е.){{(data|filter:query|filter:query2).length}}
чакеда
45

Самый простой способ, если у вас есть

<div ng-repeat="person in data | filter: query"></div>

Длина отфильтрованных данных

<div>{{ (data | filter: query).length }}</div>
ionescho
источник
Это наиболее полезно для любого, кто использует угловую директиву утилит dir-paginate, поскольку псевдонимы и альтернатива pre ng-1.3 не поддерживаются.
pcnate
Будет ли это перечислять данные дважды?
Jeppe
36

ngRepeat создает копию массива, когда применяет фильтр, поэтому вы не можете использовать исходный массив для ссылки только на отфильтрованные элементы.

В вашем случае может быть лучше применить фильтр внутри вашего контроллера, используя $filterсервис:

function MainCtrl( $scope, filterFilter ) {
  // ...

  $scope.filteredData = myNormalData;

  $scope.$watch( 'myInputModel', function ( val ) {
    $scope.filteredData = filterFilter( myNormalData, val );
  });

  // ...
}

И тогда вы используете filteredDataсвойство в вашем представлении вместо этого. Вот рабочий Плункер: http://plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview

Джош Дэвид Миллер
источник
1
Я понял, что я использую {{filteredData.length}}вместо data.length. Я также понял, что myInputModelмодель сопоставлена ​​с входным запросом, который фильтрует данные. valбудет текст, который вводится во ввод (в основном запрос), но каждый раз, когда я печатаю, он меняется. Но что filterFilterздесь? Я мог бы добавить, что у меня есть свой собственный customFilter (в котором я могу указать, какой ключ объекта следует учитывать при фильтрации).
user109187
Вот так. Также просто используйте, так ng-repeat="person in filteredData"как нет смысла фильтровать его дважды. С $filterобслуживанием, вы можете задать для конкретного фильтра, просто суффикса его «Фильтр», например: dateFilter, jsonFilterи т.д. Если вы используете свой собственный фильтр, просто используйте один вместо родового filterFilter.
Джош Дэвид Миллер
Как насчет применения нескольких фильтров к одному ng-повтору, скажем, у вас есть 2 выпадающих списка со значениями и одно поле ввода текста?
Алхимика
Фильтр - это просто функция, поэтому вы просто передаете выходные данные первого фильтра второму фильтру.
Джош Дэвид Миллер
29

Начиная с AngularJS 1.3 вы можете использовать псевдонимы:

item in items | filter:x as results

и где-то:

<span>Total {{results.length}} result(s).</span>

Из документов :

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

Например: элемент в элементах | Фильтр: x в качестве результатов сохранит фрагмент повторяющихся элементов как результаты, но только после того, как элементы были обработаны через фильтр.

Добро пожаловать в
источник
Я не могу применить $scope.$watchэтот псевдоним результат. Любые советы по $broadcastиспользованию псевдонима resultsпеременной?
DeBraid
25

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

all items: {{items.length}}
filtered items: {{filteredItems.length}}
limited and filtered items: {{limitedAndFilteredItems.length}}
<div ng-repeat="item in limitedAndFilteredItems = (filteredItems = (items | filter:search) | limitTo:25)">...</div>

вот демо скрипка

JeremyWeir
источник
13

Вот сработал пример смотри на Plunker

  <body ng-controller="MainCtrl">
    <input ng-model="search" type="text">
    <br>
    Showing {{data.length}} Persons; <br>
    Filtered {{counted}}
    <ul>
      <li ng-repeat="person in data | filter:search">
        {{person.name}}
      </li>
    </ul>
  </body>

<script> 
var app = angular.module('angularjs-starter', [])

app.controller('MainCtrl', function($scope, $filter) {
  $scope.data = [
    {
      "name": "Jim", "age" : 21
    }, {
      "name": "Jerry", "age": 26
    }, {
      "name": "Alex",  "age" : 25
    }, {
      "name": "Max", "age": 22
    }
  ];

  $scope.counted = $scope.data.length; 
  $scope.$watch("search", function(query){
    $scope.counted = $filter("filter")($scope.data, query).length;
  });
});

Maksym
источник
4
Проблема с этим подходом состоит в том, что вы запускаете фильтр дважды: один раз в ngRepeat и один раз в контроллере.
Джош Дэвид Миллер
Да, вы правы, можете ли вы опубликовать свой рабочий пример на основе вашего ответа, я хотел бы узнать немного больше фильтров? Спасибо.
Максим
2
Конечно. Вот рабочий Plunker: plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview . Я только что отредактировал твой.
Джош Дэвид Миллер
10

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

<ul>
  <li data-ng-repeat="user in usersList = (users | gender:filterGender)" data-ng-bind="user.name"></li>
</ul>
 ....
<span>{{ usersList.length | number }}</span>

Если вам нужны примеры, посмотрите примеры / демоверсии отфильтрованного количества AngularJs

Хазарапет Тунанян
источник