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?


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 .


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>

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>

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>

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


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.


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>

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>

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>

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>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.