Jak wyświetlić długość przefiltrowanych danych z powtórzeniami ng

438

Mam tablicę danych, która zawiera wiele obiektów (format JSON). Jako zawartość tej tablicy można założyć:

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

Teraz wyświetlam te szczegóły jako:

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

Tutaj zapytanie jest modelowane do pola wejściowego, w którym użytkownik może ograniczyć wyświetlane dane.

Teraz mam inną lokalizację, w której wyświetlam bieżącą liczbę wyświetlanych osób / osób, tj Showing {{data.length}} Persons

Chcę, aby gdy użytkownik szukał osoby, a wyświetlane dane były filtrowane na podstawie zapytania, Showing...personszmieniałem również wartość wyświetlanych osób. Ale tak się nie dzieje. Zawsze wyświetla całkowitą liczbę osób w danych zamiast filtrowanej - jak uzyskać liczbę odfiltrowanych danych?

użytkownik109187
źródło

Odpowiedzi:

746

Dla Angular 1.3+ (podziękowania dla @Tom)

Użyj wyrażenia aliasowego (Docs: Angular 1.3.0: ngPowtórz , przewiń w dół do sekcji Argumenty ):

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

Dla Angulara przed 1.3

Przypisz wyniki do nowej zmiennej (np. filtered) I uzyskaj do niej dostęp:

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

Wyświetl liczbę wyników:

Showing {{filtered.length}} Persons

Fiddle podobny przykład . Kredyty należą do Pawła Kozłowskiego

Wumms
źródło
42
To prosta odpowiedź, która nie powtarza wykonania filtra.
agmin
3
@Ben: Tak. Możesz uzyskać do niego dostęp za pomocą kontrolera $scope.filtered.length.
Wumms,
2
To mi nie działa. Dzienniki undefined, prawdopodobnie dlatego, że w momencie logowania zmienna nie jest jeszcze zdefiniowana. Jak więc upewnić się, że kod w kontrolerze jest uruchamiany po zdefiniowaniu zmiennej i zastosowaniu filtra w widoku?
Ben
6
@Ben: Myślę, że to nie na temat. Zastanowiłbym się nad stworzeniem niestandardowego filtra, np. Jsfiddle , można jednak również obejrzeć go w kontrolerze:$scope.$watch('filtered', function() { console.log('filtered:', $scope.filtered); });
Wumms
3
Wersja 1.3 + nie działa, jeśli chciałem dodać limitTo: person in data | filter:query as filtered | limitTo:5. Musiałem więc użyć wcześniejszej wersji 1.3:person in filtered = (data | filter: query) | limitTo: 5
benjovanic
332

Dla kompletności, oprócz poprzednich odpowiedzi (wykonaj obliczenia widocznych osób wewnątrz kontrolera), możesz również wykonać te obliczenia w szablonie HTML, jak w poniższym przykładzie.

Zakładając, że twoja lista osób jest datazmienna, a ty filtrujesz ludzi za pomocą querymodelu, następujący kod będzie dla ciebie działał:

<p>Number of visible people: {{(data|filter:query).length}}</p>
<p>Total number of people: {{data.length}}</p>
  • {{data.length}} - drukuje całkowitą liczbę osób
  • {{(data|filter:query).length}} - drukuje przefiltrowaną liczbę osób

Pamiętaj, że to rozwiązanie działa dobrze, jeśli chcesz użyć przefiltrowanych danych tylko raz na stronie. Jeśli jednak użyjesz przefiltrowanych danych więcej niż raz, np. Do prezentacji elementów i wyświetlenia długości przefiltrowanej listy, sugerowałbym użycie wyrażenia aliasowego (opisanego poniżej) dla AngularJS 1.3+ lub rozwiązania zaproponowanego przez @Wumms dla AngularJS w wersji wcześniejszej niż 1.3.

Nowa funkcja w Angular 1.3

Twórcy AngularJS również zauważyli ten problem, aw wersji 1.3 (beta 17) dodali wyrażenie „alias”, które będzie przechowywać pośrednie wyniki przemiennika po zastosowaniu filtrów, np.

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

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

Wyrażenie alias będzie zapobiec wielu kwestii wykonania filtra.

Mam nadzieję, że to pomoże.

Tomek
źródło
4
Czy to oznacza, że ​​filtr jest wykonywany dwukrotnie?
Błysk
9
Tak, jest wykonywany dwukrotnie.
nathancahill
11
upewnij się, że przeczytałeś odpowiedź @Wumms, zanim zdecydujesz się użyć tego (a nie będziesz) ...
epeleg
1
Nieważne, widzę, że odpowiedź nie zawierała części o wyrażeniu aliasu, kiedy skomentowałeś.
Adam Goodwin,
2
Ta odpowiedź obsługuje wiele wyrażeń filtrów, w przeciwieństwie do bieżącej najwyższej odpowiedzi. ie){{(data|filter:query|filter:query2).length}}
czakeda
45

Najłatwiejszy sposób, jeśli masz

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

Filtrowana długość danych

<div>{{ (data | filter: query).length }}</div>
ionescho
źródło
Jest to najbardziej przydatne dla każdego, kto używa dyrektywy dir-paginate dla narzędzi kątowych, ponieważ aliasing i alternatywa dla wersji wcześniejszej niż 1.3 nie są obsługiwane.
pcnate
Czy to wyliczy dane dwukrotnie?
Jeppe,
36

ngRepeat tworzy kopię tablicy po zastosowaniu filtru, więc nie można używać tablicy źródłowej do odwoływania się tylko do przefiltrowanych elementów.

W twoim przypadku może być lepiej zastosować filtr wewnątrz kontrolera za pomocą $filterusługi:

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

  $scope.filteredData = myNormalData;

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

  // ...
}

I filteredDatazamiast tego używasz właściwości w swoim widoku. Oto działający Plunker: http://plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview

Josh David Miller
źródło
1
Zrozumiałem, że używam {{filteredData.length}}zamiast data.length. Zrozumiałem również, że myInputModeljest to model odwzorowany na zapytanie wejściowe, które filtruje dane. valbyłby tekst wpisany w danych wejściowych (w zasadzie zapytanie), ale za każdym razem, gdy piszę, zmienia się. Ale co filterFiltertu jest ? Mogę dodać, że utworzyłem własny filtr niestandardowy (w którym mogę określić klucz obiektu, który należy rozważyć do filtrowania).
user109187
Zgadza się. Użyj również, ng-repeat="person in filteredData"ponieważ nie ma sensu filtrować go dwukrotnie. Z $filterusługi można poprosić o określonej przez filtr tylko przyrostka go z „Filtr”, np: dateFilter, jsonFilter, itd. Jeśli używasz swój własny filtr, wystarczy użyć tamten zamiast rodzajowy filterFilter.
Josh David Miller,
Co powiesz na zastosowanie wielu filtrów do jednego powtórzenia ng, powiedzmy, że masz 2 listy rozwijane z wartościami i jedno pole wprowadzania tekstu?
alchemikacja
Filtr jest tylko funkcją, więc wystarczy przekazać wynik pierwszego filtra do drugiego.
Josh David Miller
29

Od AngularJS 1.3 możesz używać aliasów:

item in items | filter:x as results

i gdzieś:

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

Z dokumentów :

Możesz także podać opcjonalne wyrażenie aliasowe, które będzie przechowywać pośrednie wyniki przemiennika po zastosowaniu filtrów. Zwykle służy to do renderowania specjalnego komunikatu, gdy filtr jest aktywny w repeaterze, ale zestaw wyników filtrowania jest pusty.

Na przykład: item in items | filter: x as wyniki będą przechowywać fragment powtarzanych elementów jako wyniki, ale dopiero po przetworzeniu elementów przez filtr.

Witamy w
źródło
Nie mogę zastosować się $scope.$watchdo tego aliasu wyniku. Jakieś wskazówki dotyczące $broadcastwprowadzania resultszmiennej aliasu ?
DeBraid
25

Warto również zauważyć, że można przechowywać wiele poziomów wyników, grupując filtry

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>

oto skrzypka demonstracyjna

JeremyWeir
źródło
13

Oto działający przykład Zobacz na 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
źródło
4
Problem z tym podejściem polega na tym, że filtr jest uruchamiany dwa razy: raz w ngRepeat i raz w kontrolerze.
Josh David Miller
Tak, masz rację, czy możesz zamieścić swój działający przykład na podstawie swojej odpowiedzi, chciałbym nauczyć się nieco więcej filtrów? Dzięki.
Maksym
2
Pewnie. Oto działający Plunker: plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview . Właśnie edytowałem twoje.
Josh David Miller,
10

Możesz to zrobić na 2 sposoby . W szablonie iw kontrolerze . W szablonie możesz ustawić filtrowaną tablicę na inną zmienną, a następnie użyć jej tak, jak chcesz. Oto jak to zrobić:

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

Jeśli potrzebujesz przykładów, zapoznaj się z filtrowanymi przykładami / pokazami AngularJs

Hazarapet Tunanyan
źródło