Como exibir o comprimento dos dados de repetição ng filtrados

438

Eu tenho uma matriz de dados que contém muitos objetos (formato JSON). O seguinte pode ser assumido como o conteúdo dessa matriz:

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

Agora, eu mostro esses detalhes como:

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

Aqui, a consulta é modelada para um campo de entrada no qual o usuário pode restringir os dados exibidos.

Agora, tenho outro local no qual mostro a contagem atual de pessoas / pessoa sendo exibida, ou seja, Showing {{data.length}} Persons

O que eu quero fazer é que, quando o usuário procurar uma pessoa e os dados exibidos forem filtrados com base na consulta, eles Showing...personstambém alterem o valor das pessoas exibidas no momento. Mas isso não está acontecendo. Ele sempre exibe o total de pessoas nos dados e não no filtrado - como obtenho a contagem de dados filtrados?

user109187
fonte

Respostas:

746

Para Angular 1.3+ (créditos para @Tom)

Use uma expressão de alias (Docs: Angular 1.3.0: ngRepeat , role para baixo até a seção Argumentos ):

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

Para Angular anterior a 1.3

Atribua os resultados a uma nova variável (por exemplo filtered) e acesse-a:

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

Exiba o número de resultados:

Showing {{filtered.length}} Persons

Veja um exemplo semelhante . Os créditos vão para Pawel Kozlowski

Wumms
fonte
42
Esta é a resposta simples que não repete a execução do filtro.
agmin
3
@ Ben: Sim. Você pode acessá-lo dentro do controlador usando $scope.filtered.length.
Wumms 18/03/2014
2
Isso não funciona para mim. Logs undefined, provavelmente porque no momento do logon, a variável ainda não está definida. Então, como garantir que o código no controlador seja executado depois que a variável foi definida e o filtro foi aplicado na exibição?
Ben
6
@ Ben: Eu acho que isso está saindo do tópico. Eu consideraria criar um filtro personalizado, por exemplo , jsfiddle , no entanto, você também pode vê-lo dentro do controlador:$scope.$watch('filtered', function() { console.log('filtered:', $scope.filtered); });
Wumms
3
A versão 1.3+ não funcionou se eu queria acrescentar limitTo: person in data | filter:query as filtered | limitTo:5. Então eu tive que usar a versão anterior a 1.3:person in filtered = (data | filter: query) | limitTo: 5
benjovanic
332

Para completar, além das respostas anteriores (execute o cálculo de pessoas visíveis dentro do controlador), você também pode executar esses cálculos no seu modelo HTML, como no exemplo abaixo.

Supondo que sua lista de pessoas seja datavariável e você filtre as pessoas usando o querymodelo, o código a seguir funcionará para você:

<p>Number of visible people: {{(data|filter:query).length}}</p>
<p>Total number of people: {{data.length}}</p>
  • {{data.length}} - imprime o número total de pessoas
  • {{(data|filter:query).length}} - imprime número filtrado de pessoas

Observe que esta solução funciona bem se você deseja usar dados filtrados apenas uma vez em uma página. No entanto, se você usar dados filtrados mais de uma vez, por exemplo, para apresentar itens e mostrar o comprimento da lista filtrada, sugiro usar a expressão de alias (descrita abaixo) para o AngularJS 1.3+ ou a solução proposta pela versão do @Wumms for AngularJS anterior à 1.3.

Novo recurso no Angular 1.3

Os criadores do AngularJS também notaram que o problema e na versão 1.3 (beta 17) eles adicionaram a expressão "alias" que armazenará os resultados intermediários do repetidor após a aplicação dos filtros, por exemplo

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

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

A expressão de alias impedirá vários problemas de execução de filtro.

Espero que ajude.

Tom
fonte
4
Isso significa que o filtro é executado duas vezes?
Flash
9
Sim, é executado duas vezes.
Nathancahill
11
certifique-se de ler @Wumms resposta antes de você decidir usar esta (e você não vai) ...
epeleg
1
Deixa pra lá, vejo que a resposta não continha a parte sobre a expressão de alias quando você fez seu comentário.
Adam Goodwin
2
Esta resposta oferece suporte a várias expressões de filtro, diferentemente da resposta principal atual. ou seja){{(data|filter:query|filter:query2).length}}
chakeda 17/05
45

A maneira mais fácil se você tiver

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

Comprimento dos dados filtrados

<div>{{ (data | filter: query).length }}</div>
ionescho
fonte
Isso é mais útil para quem usa a diretiva dir-paginate angular utils, pois o alias e a alternativa pre-1.3 não são suportados.
pcnate
Isso enumerará os dados duas vezes?
Jeppe
36

O ngRepeat cria uma cópia da matriz quando aplica um filtro, portanto você não pode usar a matriz de origem para referenciar apenas os elementos filtrados.

No seu caso, pode ser melhor aplicar o filtro dentro do seu controlador usando o $filterserviço:

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

  $scope.filteredData = myNormalData;

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

  // ...
}

E então você usa a filteredDatapropriedade em sua exibição. Aqui está um Plunker em funcionamento: http://plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview

Josh David Miller
fonte
1
O que eu entendi é que eu uso em {{filteredData.length}}vez de data.length. Eu também entendi que myInputModelé o modelo mapeado para a consulta de entrada que filtra os dados. valseria o texto digitado na entrada (basicamente consulta), mas cada vez que digito, ele muda. Mas o que tem filterFilteraqui? Devo acrescentar que eu criei meu próprio customFilter (onde posso especificar qual chave do objeto considerar para filtragem).
user109187
Está certo. Também basta usar, ng-repeat="person in filteredData"pois não faz sentido filtrá-lo duas vezes. Com o $filterserviço, você pode pedir um filtro específico por apenas suffixing-lo com "Filter", por exemplo: dateFilter, jsonFilter, etc. Se você estiver usando o seu próprio filtro personalizado, basta usar que um em vez do genérico filterFilter.
9788 Josh David Miller
Que tal aplicar vários filtros a uma repetição ng, digamos que você tenha 2 caixas suspensas com valores e um campo de entrada de texto?
alchemication
O filtro é apenas uma função, então você deve passar a saída do primeiro filtro para o segundo filtro.
21413 Josh David Miller
29

Desde o AngularJS 1.3, você pode usar aliases:

item in items | filter:x as results

e em algum lugar:

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

Dos documentos :

Você também pode fornecer uma expressão de alias opcional, que armazenará os resultados intermediários do repetidor após a aplicação dos filtros. Normalmente, isso é usado para renderizar uma mensagem especial quando um filtro está ativo no repetidor, mas o conjunto de resultados filtrados está vazio.

Por exemplo: item nos itens | filter: x como resultados armazenará o fragmento dos itens repetidos como resultados, mas somente após os itens terem sido processados ​​pelo filtro.

Bem-vindo ao
fonte
Não consigo aplicar $scope.$watchesse resultado com alias. Alguma dica para $broadcasta resultsvariável com alias ?
21816 DeBraid
25

Também é útil observar que você pode armazenar vários níveis de resultados agrupando filtros

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>

aqui está um violino demo

JeremyWeir
fonte
13

Aqui está um exemplo trabalhado Veja no 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
fonte
4
O problema com essa abordagem é que você está executando o filtro duas vezes: uma vez no ngRepeat e outra no controlador.
Josh David Miller
Sim, você está certo, você pode postar seu exemplo de trabalho com base na sua resposta, eu gostaria de aprender um pouco mais de filtros? Obrigado.
Maksym
2
Certo. Aqui está o Plunker em funcionamento: plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview . Acabei de editar o seu.
Josh David Miller
10

Você pode fazer isso de duas maneiras . No modelo e no Controlador . No modelo, você pode definir sua matriz filtrada para outra variável e usá-la como desejar. Aqui está como fazê-lo:

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

Se você precisar de exemplos, consulte os exemplos / demonstrações de contagem filtrada do AngularJs

Hazarapet Tunanyan
fonte