AngularJS ng-repeat handle cas de liste vide

377

Je pensais que ce serait une chose très courante, mais je n'ai pas trouvé comment le gérer dans AngularJS. Disons que j'ai une liste d'événements et que je veux les sortir avec AngularJS, alors c'est assez simple:

<ul>
    <li ng-repeat="event in events">{{event.title}}</li>
</ul>

Mais comment gérer le cas lorsque la liste est vide? Je veux avoir une boîte de message en place où la liste est avec quelque chose comme "Aucun événement" ou similaire. La seule chose qui se rapprocherait est le ng-switchavec events.length(comment puis-je vérifier s'il est vide quand un objet et non un tableau?), Mais est-ce vraiment la seule option que j'ai?

Prinzhorn
la source
4
@ La réponse d'Artem est bonne (+1). Voici une discussion de groupe Google qui utilise un filtre, pour référence / comparaison: groups.google.com/d/topic/angular/wR06cN5oVBQ/discussion
Mark Rajcok

Réponses:

569

Vous pouvez utiliser ngShow .

<li ng-show="!events.length">No events</li>

Voir l' exemple .

Ou vous pouvez utiliser ngHide

<li ng-hide="events.length">No events</li>

Voir l' exemple .

Pour objet, vous pouvez tester Object.keys .

Artem Andreev
la source
1
En effet @ArtemAndreev. Lorsque "events" est un tableau vide, il retourne vrai même si le tableau est vide
Rob Juurlink
Je pense qu'il y a un problème avec Object.keys: jsfiddle.net/J9b5z , comment géreriez -vous cela?
Dani
5
nh-show a fonctionné id mon cas, mais il ne met pas à jour l'état quand j'ai mis un filtre et rien n'est retourné. De plus, l'objet apparaît au premier chargement et disparaît lorsque le processus de répétition effectué au chargement de la page.
dvdmn
1
@Dani essaie d'ajouter une fonction à votre contrôleur qui effectue le test. Vous pouvez alors simplement invoquer la directive avec ng-hide="hasEvents()".
M. S
Oui merci. Cependant, j'espérais qu'il y aurait une manière plus élégante sans "polluer" le contrôleur.
Dani
370

Et si vous souhaitez l'utiliser avec une liste filtrée, voici une astuce intéressante:

<ul>
    <li ng-repeat="item in filteredItems  = (items | filter:keyword)">
        ...
    </li>
</ul>
<div ng-hide="filteredItems.length">No items found</div>
Konrad 'ktoso' Malawski
la source
3
Très utile. Typo cependant avec "filterFragments".
ravishi
1
Doux! L'expression à l'intérieur de ng-repeat semble cependant étrange. Avez-vous une chance de l'expliquer? Merci!!
MK Safi
7
@MKSafi, il crée une nouvelle variable sur la portée appelée filteredItemset définit sa valeur sur (items | filter:keyword)- en d'autres termes, le tableau renvoyé par le filtre
AlexFoxGill
17
OUI! Ninja plus de points! Cela évite angulaire d'évaluer deux fois un filtre complexe!
markmarijnissen
2
En outre, il semble y avoir des limites à cela avec plusieurs filtres, "face in filteredFaces = faces|filter:{deleted: true} | orderBy:'text'mais je suis d'accord avec tout le monde, c'est une astuce fabuleuse.
Fitter Man
29

Vous voudrez peut-être consulter la directive angular-ui ui-if si vous souhaitez simplement supprimer le uldu DOM lorsque la liste est vide:

<ul ui-if="!!events.length">
    <li ng-repeat="event in events">{{event.title}}</li>
</ul>
Mortimer
la source
1
Merci. Se sent plus propre que de simplement le cacher.
Prinzhorn
@Mortimer: dans ce cas, avons-nous besoin d'angular-ui?
Shibbir Ahmed
vous pouvez utiliser ng-hidesans angular-ui, mais cela masquera simplement le nœud, il ne le supprimera pas de l'arborescence DOM. Avec la ui-ifdirective angular-ui , il supprimera le nœud DOM. Donc, vous devez au moins ajouter la ui-ifdirective du code angular-ui à votre propre code.
Mortimer
21
le plus récent angulaire a ng-ifinclus!
markmarijnissen
4
Notez que cela ng-ifcrée une nouvelle portée, où il ng-hiden'y en a pas. Cela peut provoquer un comportement inattendu.
Arnold Daniels
29

Avec les nouvelles versions d'angularjs, la bonne réponse à cette question est d'utiliser ng-if:

<ul>
  <li ng-if="list.length === 0">( No items in this list yet! )</li>
  <li ng-repeat="item in list">{{ item }}</li>
</ul>

Cette solution ne scintillera pas non plus lorsque la liste est sur le point d'être téléchargée car la liste doit être définie et d'une longueur de 0 pour que le message s'affiche.

Voici un plongeur pour le montrer en cours d'utilisation: http://plnkr.co/edit/in7ha1wTlpuVgamiOblS?p=preview

Conseil: vous pouvez également afficher un texte de chargement ou une double flèche:

  <li ng-if="!list">( Loading... )</li>
Pylinux
la source
23
<ul>
    <li ng-repeat="item in items | filter:keyword as filteredItems">
        ...
    </li>
    <li ng-if="filteredItems.length===0">
        No items found
    </li>
</ul>

Ceci est similaire à @Konrad 'ktoso' Malawski mais légèrement plus facile à retenir.

Testé avec Angular 1.4

Bernard
la source
3
Vous voulez direng-if='!filteredItems.length'
abrunet
Comment faites-vous cela avec plusieurs filtres?
Jordash
@Jordash Continuez à les canaliser item in items | filter: ... | filter: ...
Bernard
Un raffinement agréable et supplémentaire est<li ng-if="!filteredItems.length">
Matty J
C'est bien. J'utilisais une méthode beaucoup plus sale avant commeitem in (filteredItems = (items | filter: someFilter))
Firze
6

Voici une approche différente en utilisant CSS au lieu de JavaScript / AngularJS.

CSS:

.emptymsg {
  display: list-item;
}

li + .emptymsg {
  display: none;
}

Balisage:

<ul>
    <li ng-repeat="item in filteredItems"> ... </li>
    <li class="emptymsg">No items found</li>
</ul>

Si la liste est vide, <li ng-repeat = "item in filtersItems">, etc. sera mis en commentaire et deviendra un commentaire au lieu d'un élément li.

Miriam Salzer
la source
La question dit "Je veux avoir une boîte de message à l'endroit où se trouve la liste". Je pense également qu'il est désavantageux de séparer la logique dans la feuille de style. Difficile à entretenir et à demander des ennuis.
Prinzhorn
1
@Prinzhorn, je pense que l'utilisation de CSS est un avantage car la logique est très simple et facile à maintenir, le CSS est réutilisable pour d'autres listes, et il ne repose pas sur JavaScript. Aucun écouteur ou observateur supplémentaire n'est nécessaire. Le message pourrait être conçu pour ressembler à une boîte, je n'ai simplement pas gardé la réponse simple.
Miriam Salzer
Quelques mois de retard, d'accord, mais je suis d'accord avec Miriam, je pense que cette réponse est géniale.
Jon Combe
2

Vous pouvez utiliser ce ng-switch:

<div ng-app ng-controller="friendsCtrl">
  <label>Search: </label><input ng-model="searchText" type="text">
  <div ng-init="filtered = (friends | filter:searchText)">
  <h3>'Found '{{(friends | filter:searchText).length}} friends</h3>
  <div ng-switch="(friends | filter:searchText).length">
    <span class="ng-empty" ng-switch-when="0">No friends</span>
    <table ng-switch-default>
      <thead>  
        <tr>
          <th>Name</th>
          <th>Phone</th>
        </tr>
      </thead>
      <tbody>
      <tr ng-repeat="friend in friends | filter:searchText">
        <td>{{friend.name}}</td>
        <td>{{friend.phone}}</td>
      </tr>
    </tbody>
  </table>
</div>
LukitaBrands
la source
1

Vous pouvez utiliser un asmot-clé pour référencer une collection sous un ng-repeatélément:

<table>
    <tr ng-repeat="task in tasks | filter:category | filter:query as res">
        <td>{{task.id}}</td>
        <td>{{task.description}}</td>
    </tr>
    <tr ng-if="res.length === 0">
        <td colspan="2">no results</td>
    </tr>
</table>
Damian Czapiewski
la source
0

j'utilise habituellement ng-show

<li ng-show="variable.length"></li>

où variable vous définissez par exemple

<div class="list-group-item" ng-repeat="product in store.products">
   <li ng-show="product.length">show something</li>
</div>
Ezequiel García
la source
0

vous pouvez utiliser ng-if car ce n'est pas le rendu dans la page html et vous ne voyez pas votre balise html dans inspect ...

<ul ng-repeat="item in items" ng-if="items.length > 0">
    <li>{{item}}<li>
</ul>
<div class="alert alert-info">there is no items!</div>
pejman
la source