Comment afficher la longueur des données de répétition ng filtrées

438

J'ai un tableau de données qui contient de nombreux objets (format JSON). Les éléments suivants peuvent être considérés comme le contenu de ce tableau:

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

Maintenant, j'affiche ces détails comme:

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

Ici, la requête est modélisée dans un champ de saisie dans lequel l'utilisateur peut restreindre les données affichées.

Maintenant, j'ai un autre emplacement dans lequel j'affiche le nombre actuel de personnes / personne étant affiché, à savoir Showing {{data.length}} Persons

Ce que je veux faire, c'est que lorsque l'utilisateur recherche une personne et que les données affichées sont filtrées en fonction de la requête, cela Showing...personsmodifie également la valeur des personnes affichées actuellement. Mais cela ne se produit pas. Il affiche toujours le nombre total de personnes dans les données plutôt que celui filtré - comment puis-je obtenir le nombre de données filtrées?

user109187
la source

Réponses:

746

Pour Angular 1.3+ (crédits à @Tom)

Utilisez une expression d'alias (Docs: Angular 1.3.0: ngRepeat , faites défiler jusqu'à la section Arguments ):

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

Pour Angular avant 1.3

Attribuez les résultats à une nouvelle variable (par exemple filtered) et accédez-y:

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

Afficher le nombre de résultats:

Showing {{filtered.length}} Persons

Fiddle un exemple similaire . Les crédits vont à Pawel Kozlowski

Wumms
la source
42
C'est la réponse simple qui ne répète pas l'exécution du filtre.
agmin
3
@Ben: Oui. Vous pouvez y accéder à l'intérieur du contrôleur à l'aide de $scope.filtered.length.
Wumms
2
Ça ne marche pas pour moi. Journaux undefined, probablement parce qu'au moment de la journalisation, la variable n'est pas encore définie. Alors, comment puis-je m'assurer que le code dans le contrôleur est exécuté après la définition de la variable et l'application du filtre dans la vue?
Ben
6
@Ben: Je suppose que cela va hors sujet. J'envisagerais de créer un filtre personnalisé, par exemple jsfiddle , cependant, vous pouvez également le regarder à l'intérieur du contrôleur:$scope.$watch('filtered', function() { console.log('filtered:', $scope.filtered); });
Wumms
3
La version 1.3+ ne fonctionne pas si je voulais ajouter LimitTo: person in data | filter:query as filtered | limitTo:5. J'ai donc dû utiliser la version antérieure à 1.3:person in filtered = (data | filter: query) | limitTo: 5
benjovanic
333

Pour être complet, en plus des réponses précédentes (effectuer le calcul des personnes visibles à l'intérieur du contrôleur), vous pouvez également effectuer ces calculs dans votre modèle HTML comme dans l'exemple ci-dessous.

En supposant que votre liste de personnes est datavariable et que vous filtrez les personnes à l'aide du querymodèle, le code suivant fonctionnera pour vous:

<p>Number of visible people: {{(data|filter:query).length}}</p>
<p>Total number of people: {{data.length}}</p>
  • {{data.length}} - imprime le nombre total de personnes
  • {{(data|filter:query).length}} - imprime le nombre de personnes filtré

Notez que cette solution fonctionne correctement si vous souhaitez utiliser les données filtrées une seule fois dans une page. Cependant, si vous utilisez des données filtrées plus d'une fois, par exemple pour présenter des éléments et afficher la longueur de la liste filtrée, je suggère d'utiliser l' expression d'alias (décrite ci-dessous) pour AngularJS 1.3+ ou la solution proposée par @Wumms pour AngularJS version antérieure à 1.3.

Nouvelle fonctionnalité dans Angular 1.3

Les créateurs d'AngularJS ont également remarqué ce problème et dans la version 1.3 (beta 17), ils ont ajouté l'expression "alias" qui stockera les résultats intermédiaires du répéteur après l'application des filtres, par exemple

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

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

L' expression d'alias empêchera plusieurs problèmes d'exécution de filtre.

J'espère que cela aidera.

À M
la source
4
Cela signifie-t-il que le filtre est exécuté deux fois?
Flash
9
Oui, il est exécuté deux fois.
nathancahill
11
assurez-vous de lire la réponse @Wumms avant de décider d'utiliser celle-ci (et vous ne le ferez pas) ...
epeleg
1
Peu importe, je vois que la réponse ne contenait pas la partie concernant l'expression d'alias lorsque vous avez fait votre commentaire.
Adam Goodwin
2
Cette réponse prend en charge plusieurs expressions de filtre contrairement à la première réponse actuelle. ie){{(data|filter:query|filter:query2).length}}
chakeda
45

La façon la plus simple si vous avez

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

Longueur des données filtrées

<div>{{ (data | filter: query).length }}</div>
ionescho
la source
Ceci est très utile pour quiconque utilise la directive dir-paginate d'utilitaires angulaires car l'aliasing et l'alternative pre ng-1.3 ne sont pas pris en charge.
pcnate
Cela énumérera-t-il les données deux fois?
Jeppe
36

ngRepeat crée une copie du tableau lorsqu'il applique un filtre, vous ne pouvez donc pas utiliser le tableau source pour référencer uniquement les éléments filtrés.

Dans votre cas, il peut être préférable d'appliquer le filtre à l'intérieur de votre contrôleur en utilisant le $filterservice:

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

  $scope.filteredData = myNormalData;

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

  // ...
}

Et puis vous utilisez la filteredDatapropriété dans votre vue à la place. Voici un Plunker fonctionnel: http://plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview

Josh David Miller
la source
1
Ce que j'ai compris, c'est que j'utilise à la {{filteredData.length}}place de data.length. J'ai également compris que myInputModelc'est le modèle mappé à la requête d'entrée qui filtre les données. valserait le texte qui est tapé dans l'entrée (essentiellement une requête) mais chaque fois que je tape, il change. Mais qu'y a- filterFiltert-il ici? Je pourrais ajouter que j'ai créé mon propre filtre personnalisé (dans lequel je peux spécifier la clé de l'objet à prendre en compte pour le filtrage).
user109187
C'est vrai. Utilisez-le également ng-repeat="person in filteredData"car il n'y a aucun sens à le filtrer deux fois. Avec le $filterservice, vous pouvez demander un filtre spécifique en tout suffixant avec « Filtre », par exemple: dateFilter, jsonFilter, etc. Si vous utilisez votre propre filtre personnalisé, il suffit d' utiliser que l' on place du générique filterFilter.
Josh David Miller
Qu'en est-il de l'application de plusieurs filtres à une répétition ng, disons que vous avez 2 listes déroulantes avec des valeurs et un champ de saisie de texte?
alchemication
Le filtre n'est qu'une fonction, vous devez donc simplement passer la sortie du premier filtre au deuxième filtre.
Josh David Miller
29

Depuis AngularJS 1.3, vous pouvez utiliser des alias:

item in items | filter:x as results

et quelque part:

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

Depuis les documents :

Vous pouvez également fournir une expression d'alias facultative qui stockera ensuite les résultats intermédiaires du répéteur après l'application des filtres. Il est généralement utilisé pour afficher un message spécial lorsqu'un filtre est actif sur le répéteur, mais que l'ensemble de résultats filtré est vide.

Par exemple: élément dans les éléments | filter: x as results stockera le fragment des éléments répétés en tant que résultats, mais uniquement après que les éléments auront été traités via le filtre.

Bienvenue à
la source
Je ne peux pas postuler $scope.$watchà ce résultat aliasé. Des conseils pour $broadcasting la resultsvariable aliasée ?
DeBraid
25

Il est également utile de noter que vous pouvez stocker plusieurs niveaux de résultats en regroupant les filtres

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>

voici un violon de démonstration

JeremyWeir
la source
13

Voici l'exemple travaillé Voir sur 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
la source
4
Le problème avec cette approche est que vous exécutez le filtre deux fois: une fois dans ngRepeat et une fois dans le contrôleur.
Josh David Miller
Oui, vous avez raison, pouvez-vous publier votre exemple de travail en fonction de votre réponse, je voudrais en savoir un peu plus sur les filtres? Merci.
Maksym
2
Sûr. Voici le Plunker qui fonctionne: plnkr.co/edit/7c1l24rPkuKPOS5o2qtx?p=preview . Je viens de modifier le vôtre.
Josh David Miller
10

Vous pouvez le faire de 2 manières . Dans le modèle et dans le contrôleur . Dans le modèle, vous pouvez définir votre tableau filtré sur une autre variable, puis l'utiliser comme vous le souhaitez. Voici comment faire:

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

Si vous avez besoin d'exemples, consultez les exemples / démos de comptage filtré AngularJs

Hazarapet Tunanyan
la source