AngularJS ng-repeat handle caso de lista vacía

377

Pensé que esto sería algo muy común, pero no pude encontrar cómo manejarlo en AngularJS. Digamos que tengo una lista de eventos y quiero generarlos con AngularJS, entonces eso es bastante fácil:

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

Pero, ¿cómo manejo el caso cuando la lista está vacía? Quiero tener un cuadro de mensaje en el lugar donde está la lista con algo como "Sin eventos" o similar. Lo único que se acercaría es ng-switchcon events.length(¿cómo verifico si está vacío cuando hay un objeto y no una matriz?), Pero ¿es realmente la única opción que tengo?

Prinzhorn
fuente
44
La respuesta de @ Artem es buena (+1). Aquí hay una discusión grupal de Google que usa un filtro, para referencia / comparación: groups.google.com/d/topic/angular/wR06cN5oVBQ/discussion
Mark Rajcok

Respuestas:

569

Puedes usar ngShow .

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

Ver ejemplo .

O puedes usar ngHide

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

Ver ejemplo .

Para el objeto, puede probar Object.keys .

Artem Andreev
fuente
1
De hecho @ArtemAndreev. Cuando "eventos" es una matriz vacía, devuelve verdadero a pesar de que la matriz está vacía
Rob Juurlink
Creo que hay un problema con Object.keys: jsfiddle.net/J9b5z , ¿cómo manejarías esto?
Dani
55
nh-show funcionó id mi caso, pero no actualiza el estado cuando puse un filtro y no devolvió nada. Además, el objeto aparece en la primera carga y desaparece cuando el proceso de repetición realizado en la carga de la página.
dvdmn
1
@Dani intente agregar una función a su controlador que realice la prueba. Luego puede invocar la directiva con ng-hide="hasEvents()".
Sr. S
Si gracias. Sin embargo, esperaba que hubiera una forma más elegante sin "contaminar" el controlador.
Dani
370

Y si quieres usar esto con una lista filtrada, aquí hay un buen truco:

<ul>
    <li ng-repeat="item in filteredItems  = (items | filter:keyword)">
        ...
    </li>
</ul>
<div ng-hide="filteredItems.length">No items found</div>
Konrad 'ktoso' Malawski
fuente
3
Muy útil. Error tipográfico con "Fragmentos filtrados".
Ravishi
1
¡Dulce! Sin embargo, la expresión dentro de ng-repeat parece extraña. ¿Alguna posibilidad de que puedas explicarlo? ¡¡Gracias!!
MK Safi
77
@MKSafi, está creando una nueva variable en el ámbito llamado filteredItemsy configurando su valor para (items | filter:keyword), en otras palabras, la matriz devuelta por el filtro
AlexFoxGill el
17
¡SI! Ninja plus puntos! ¡Esto ahorra angular al evaluar un filtro complejo dos veces!
markmarijnissen
2
Además, parece haber algunas limitaciones en torno a esto con múltiples filtros, "face in filteredFaces = faces|filter:{deleted: true} | orderBy:'text'pero estoy de acuerdo con todos, este es un truco fabuloso.
Fitter Man
29

Es posible que desee consultar la directiva angular-ui ui-if si solo desea eliminar ulel DOM cuando la lista está vacía:

<ul ui-if="!!events.length">
    <li ng-repeat="event in events">{{event.title}}</li>
</ul>
Mortimer
fuente
1
Gracias. Se siente más limpio que solo esconderlo.
Prinzhorn
@Mortimer: entonces, ¿en este caso necesitamos angular-ui?
Shibbir Ahmed
puede usar ng-hidesin angular-ui, pero solo ocultará el nodo, no lo eliminará del árbol DOM. Con la ui-ifdirectiva angular-ui , eliminará el nodo DOM. Por lo tanto, debe agregar al menos la ui-ifdirectiva del código angular-ui a su propio código.
Mortimer
21
más nuevo angular ha ng-ifincluido!
markmarijnissen
44
Tenga en cuenta que ng-ifestá creando un nuevo ámbito, donde ng-hideno lo está. Esto puede causar un comportamiento inesperado.
Arnold Daniels
29

Con las versiones más recientes de angularjs, la respuesta correcta a esta pregunta es usar 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>

Esta solución no parpadeará cuando la lista esté a punto de descargarse porque la lista tiene que estar definida y con una longitud de 0 para que se muestre el mensaje.

Aquí hay un plunker para mostrarlo en uso: http://plnkr.co/edit/in7ha1wTlpuVgamiOblS?p=preview

Sugerencia: También puede mostrar un texto de carga o una flecha giratoria:

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

Esto es similar a @Konrad 'ktoso' Malawski pero un poco más fácil de recordar.

Probado con Angular 1.4

Bernardo
fuente
3
¿Quieres decirng-if='!filteredItems.length'
Abrunet
¿Cómo se hace esto con múltiples filtros?
Jordash
@Jordash Solo sigue entubándolos item in items | filter: ... | filter: ...
Bernard
Un buen refinamiento adicional es<li ng-if="!filteredItems.length">
Matty J
Esto es genial. He estado usando un método de ensuciamiento mucho antes comoitem in (filteredItems = (items | filter: someFilter))
Firze
6

Aquí hay un enfoque diferente usando CSS en lugar de JavaScript / AngularJS.

CSS:

.emptymsg {
  display: list-item;
}

li + .emptymsg {
  display: none;
}

Margen:

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

Si la lista está vacía, <li ng-repeat = "item in filterItems">, etc. se comentará y se convertirá en un comentario en lugar de un elemento li.

Miriam Salzer
fuente
La pregunta dice "Quiero tener un cuadro de mensaje en el lugar donde está la lista". También creo que es desventajoso separar la lógica en la hoja de estilo de s. Difícil de mantener y pedir problemas.
Prinzhorn
1
@Prinzhorn, creo que usar CSS es una ventaja porque la lógica es muy simple y fácil de mantener, el CSS es reutilizable para otras listas y no se basa en JavaScript. No se necesitan oyentes ni observadores adicionales. El mensaje podría tener el estilo de un recuadro, simplemente no quería que la respuesta fuera simple.
Miriam Salzer el
Unos meses de retraso, concedido, pero estoy de acuerdo con Miriam, creo que esta respuesta es genial.
Jon Combe el
2

Puedes usar este 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
fuente
1

Puede usar la aspalabra clave para referir una colección bajo un ng-repeatelemento:

<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
fuente
0

yo suelo usar ng-show

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

donde variable define por ejemplo

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

puede usar ng-if porque esto no se procesa en la página html y no ve su etiqueta html en la inspección ...

<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
fuente