Cómo mostrar la longitud de los datos filtrados ng-repeat

438

Tengo una matriz de datos que contiene muchos objetos (formato JSON). Se puede suponer lo siguiente como contenido de esta matriz:

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

Ahora, muestro estos detalles como:

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

Aquí, la consulta se modela en un campo de entrada en el que el usuario puede restringir los datos que se muestran.

Ahora, tengo otra ubicación en la que visualizo el recuento actual de personas / personas que se muestran, es decir Showing {{data.length}} Persons

Lo que quiero hacer es que cuando el usuario busca a una persona y los datos que se muestran se filtran en función de la consulta, Showing...personstambién cambia el valor de las personas que se muestran actualmente. Pero no está sucediendo. Siempre muestra el total de personas en los datos en lugar del filtrado: ¿cómo obtengo el recuento de datos filtrados?

usuario109187
fuente

Respuestas:

746

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

Use una expresión de alias (Docs: Angular 1.3.0: ngRepeat , desplácese hacia abajo hasta la sección Argumentos ):

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

Para angular anterior a 1.3

Asigne los resultados a una nueva variable (por ejemplo filtered) y acceda a ella:

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

Mostrar el número de resultados:

Showing {{filtered.length}} Persons

Violín un ejemplo similar . Los créditos van a Pawel Kozlowski

Wumms
fuente
42
Esta es la respuesta simple que no repite la ejecución del filtro.
agmin
3
@Ben: sí. Puede acceder desde el controlador usando $scope.filtered.length.
Wumms
2
Eso no me funciona. Registros undefined, probablemente porque al momento de registrarlo, la variable aún no está definida. Entonces, ¿cómo podría asegurarme de que el código en el controlador se ejecuta después de que se definió la variable y se aplicó el filtro en la vista?
Ben
66
@Ben: Supongo que esto está fuera de tema. Consideraría crear un filtro personalizado, por ejemplo , jsfiddle , sin embargo, también podría verlo dentro del controlador:$scope.$watch('filtered', function() { console.log('filtered:', $scope.filtered); });
Wumms
3
La versión 1.3+ no funciona si quería añadir LimitTo: person in data | filter:query as filtered | limitTo:5. Así que tuve que usar la versión anterior a la 1.3:person in filtered = (data | filter: query) | limitTo: 5
benjovanic
333

Para completar, además de las respuestas anteriores (realizar el cálculo de personas visibles dentro del controlador) también puede realizar esos cálculos en su plantilla HTML como en el ejemplo a continuación.

Suponiendo que su lista de personas está en datavariable y que filtra personas usando el querymodelo, el siguiente código funcionará para usted:

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

Tenga en cuenta que esta solución funciona bien si desea utilizar datos filtrados solo una vez en una página. Sin embargo, si usa datos filtrados más de una vez, por ejemplo, para presentar elementos y mostrar la longitud de la lista filtrada, sugeriría usar una expresión de alias (descrita a continuación) para AngularJS 1.3+ o la solución propuesta por @Wumms para la versión de AngularJS anterior a 1.3.

Nueva característica en Angular 1.3

Los creadores de AngularJS también notaron ese problema y en la versión 1.3 (beta 17) agregaron la expresión "alias" que almacenará los resultados intermedios del repetidor después de que se hayan aplicado los filtros, por ejemplo

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

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

La expresión de alias evitará un problema de ejecución de múltiples filtros.

Espero que le ayudará.

Tom
fuente
44
¿Significará esto que el filtro se ejecuta dos veces?
Flash
99
Sí, se ejecuta dos veces.
nathancahill
11
asegúrese de leer la respuesta @Wumms antes de decidirse a utilizar este (y no lo hará) ...
epeleg
1
No importa, veo que la respuesta no contenía la parte sobre la expresión de alias cuando hiciste tu comentario.
Adam Goodwin
2
Esta respuesta admite múltiples expresiones de filtro a diferencia de la respuesta principal actual. es decir){{(data|filter:query|filter:query2).length}}
chakeda
45

La forma más fácil si tienes

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

Longitud de datos filtrados

<div>{{ (data | filter: query).length }}</div>
ionescho
fuente
Esto es más útil para cualquiera que use la directiva angular utils dir-paginate, ya que el aliasing y la alternativa pre ng-1.3 no son compatibles.
pcnate
¿Esto enumerará los datos dos veces?
Jeppe
36

ngRepeat crea una copia de la matriz cuando aplica un filtro, por lo que no puede usar la matriz fuente para hacer referencia solo a los elementos filtrados.

En su caso, puede ser mejor aplicar el filtro dentro de su controlador utilizando el $filterservicio:

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

  $scope.filteredData = myNormalData;

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

  // ...
}

Y luego usa la filteredDatapropiedad en su vista en su lugar. Aquí hay un Plunker en funcionamiento: http://plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview

Josh David Miller
fuente
1
Lo que entendí es que uso en {{filteredData.length}}lugar de data.length. También entendí quemyInputModel es el modelo asignado a la consulta de entrada que filtra los datos. valsería el texto que se escribe en la entrada (básicamente consulta) pero cada vez que escribo, cambia. Pero que hay filterFilteraqui? Podría agregar que tengo mi propio CustomFilter creado (en el que puedo especificar qué clave del objeto considerar para el filtrado).
user109187
Así es. También utilícelo ng-repeat="person in filteredData"ya que no tiene sentido filtrarlo dos veces. Con el $filterservicio, puede solicitar un filtro específico simplemente con el sufijo "Filtro", por ejemplo:dateFilter , jsonFilter, etc Si usted está utilizando su propio filtro personalizado, sólo tiene que utilizar uno que en lugar del genérico filterFilter.
Josh David Miller
¿Qué pasa con la aplicación de múltiples filtros a una repetición ng, digamos que tiene 2 menús desplegables con valores y un campo de entrada de texto?
alquimia
El filtro es solo una función, por lo que simplemente pasaría la salida del primer filtro al segundo filtro.
Josh David Miller
29

Desde AngularJS 1.3 puede usar alias:

item in items | filter:x as results

y en alguna parte:

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

De documentos :

También puede proporcionar una expresión de alias opcional que luego almacenará los resultados intermedios del repetidor después de que se hayan aplicado los filtros. Por lo general, esto se usa para representar un mensaje especial cuando un filtro está activo en el repetidor, pero el conjunto de resultados filtrado está vacío.

Por ejemplo: artículo en artículos | filter: x como resultados almacenará el fragmento de los elementos repetidos como resultados, pero solo después de que los elementos hayan sido procesados ​​a través del filtro.

Bienvenido a
fuente
No puedo aplicar $scope.$watcha este resultado con alias. ¿Algún consejo para $broadcastla resultsvariable con alias ?
DeBraid
25

También es útil tener en cuenta que puede almacenar múltiples niveles 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>

aquí hay un violín de demostración

JeremyWeir
fuente
13

Aquí está el ejemplo trabajado Ver en 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
fuente
44
El problema con este enfoque es que está ejecutando el filtro dos veces: una en ngRepeat y otra en el controlador.
Josh David Miller
Sí, tienes razón, ¿puedes publicar tu ejemplo de trabajo basado en tu respuesta, me gustaría aprender un poco más de filtros? Gracias.
Maksym
2
Por supuesto. Aquí está el Plunker de trabajo: plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview . Acabo de editar el tuyo.
Josh David Miller
10

Puedes hacerlo de 2 maneras . En plantilla y en controlador . En la plantilla, puede establecer su matriz filtrada en otra variable y luego usarla como desee. Aquí está cómo hacerlo:

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

Si necesita ejemplos, vea los ejemplos / demostraciones de conteo filtrado de AngularJs

Hazarapet Tunanyan
fuente