Caixa de listagem vazia do manipulador de repetição angularJS

377

Eu pensei que isso seria uma coisa muito comum, mas não consegui encontrar como lidar com isso no AngularJS. Digamos que eu tenho uma lista de eventos e quero produzi-los com o AngularJS, então isso é bem fácil:

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

Mas como eu manejo o caso quando a lista está vazia? Quero ter uma caixa de mensagem no lugar em que a lista esteja com algo como "Nenhum evento" ou similar. A única coisa que chegaria perto é ng-switchcom events.length(como verifico se vazio quando um objeto e não uma matriz?), Mas essa é realmente a única opção que tenho?

Prinzhorn
fonte
4
@ A resposta de Artem é boa (+1). Aqui está uma discussão em grupo do Google que usa um filtro, para referência / comparação: groups.google.com/d/topic/angular/wR06cN5oVBQ/discussion
Mark Rajcok

Respostas:

569

Você pode usar o ngShow .

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

Veja exemplo .

Ou você pode usar o ngHide

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

Veja exemplo .

Para objeto, você pode testar Object.keys .

Artem Andreev
fonte
11
De fato, @ArtemAndreev. Quando "eventos" é um array vazio, ele retorna true mesmo que a matriz está vazia
Rob Juurlink
Eu acho que há um problema com Object.keys: jsfiddle.net/J9b5z , como você lidaria com isso?
Dani
5
nh-show funcionou id meu caso, mas ele não atualiza o estado quando eu coloquei um filtro e nada retornou. Além disso, o objeto aparece no primeiro carregamento e desaparece quando o processo de repetição é feito no carregamento da página.
dvdmn
11
@Dani tente adicionar uma função ao seu controlador que execute o teste. Você pode simplesmente invocar a diretiva com ng-hide="hasEvents()".
Mr. S
Sim obrigado. No entanto, eu esperava que houvesse uma maneira mais elegante sem "poluir" o controlador.
27413 Dani
370

E se você quiser usar isso com uma lista filtrada, aqui está um truque:

<ul>
    <li ng-repeat="item in filteredItems  = (items | filter:keyword)">
        ...
    </li>
</ul>
<div ng-hide="filteredItems.length">No items found</div>
Konrad 'ktoso' Malawski
fonte
3
Muito útil. Erro de digitação no entanto com "filterFragments".
Ravishi #
11
Doce! A expressão dentro de ng-repeat parece estranha. Alguma chance de você explicar isso? Obrigado!!
MK Safi
7
@MKSafi, ele está criando uma nova variável no escopo chamado filteredItemse definindo seu valor como (items | filter:keyword)- em outras palavras, o array retornado pelo filtro
AlexFoxGill
17
SIM! Ninja mais pontos! Isso evita que o angular avalie um filtro complexo duas vezes!
markmarijnissen
2
Além disso, parece haver algumas limitações em relação a isso com vários filtros, como, "face in filteredFaces = faces|filter:{deleted: true} | orderBy:'text'mas eu concordo com todos, esse é um truque fabuloso.
Fitter Man
29

Você pode querer verificar a diretiva angular-ui ui-if se quiser remover o uldo DOM quando a lista estiver vazia:

<ul ui-if="!!events.length">
    <li ng-repeat="event in events">{{event.title}}</li>
</ul>
Mortimer
fonte
11
Obrigado. Parece mais limpo do que apenas escondê-lo.
Prinzhorn
@ Mortimer: então, neste caso, precisamos de angular-ui?
Shibbir Ahmed
você pode usar ng-hidesem angular-ui, mas ele apenas oculta o nó, não o remove da árvore DOM. Com a ui-ifdiretiva angular-ui , ele removerá o nó DOM. Portanto, você precisa pelo menos adicionar a ui-ifdiretiva do código angular-ui ao seu próprio código.
precisa
21
O mais novo angular foi ng-ifincluído!
markmarijnissen
4
Observe que ng-ifestá criando um novo escopo, onde ng-hidenão está. Isso pode causar comportamento inesperado.
Arnold Daniels
29

Com as versões mais recentes do angularjs, a resposta correta para esta pergunta é 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>

Essa solução não piscará quando a lista estiver prestes a baixar porque a lista precisa ser definida e com um comprimento de 0 para que a mensagem seja exibida.

Aqui está um plunker para mostrá-lo em uso: http://plnkr.co/edit/in7ha1wTlpuVgamiOblS?p=preview

Dica: você também pode mostrar um texto ou botão giratório de carregamento:

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

Isso é semelhante ao @Konrad 'ktoso' Malawski, mas um pouco mais fácil de lembrar.

Testado com Angular 1.4

Bernard
fonte
3
Você quer dizerng-if='!filteredItems.length'
abrunet 19/10/2015
Como você faz isso com vários filtros?
Jordash 19/05/19
@Jordash Basta manter a tubulação-los item in items | filter: ... | filter: ...
Bernard
Um refinamento bom e adicional é<li ng-if="!filteredItems.length">
Matty J
Isso é ótimo. Tenho vindo a utilizar um método muito dirtyer antes comoitem in (filteredItems = (items | filter: someFilter))
Firze
6

Aqui está uma abordagem diferente usando CSS em vez de JavaScript / AngularJS.

CSS:

.emptymsg {
  display: list-item;
}

li + .emptymsg {
  display: none;
}

Marcação:

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

Se a lista estiver vazia, o <li ng-repeat = "item no filterItems"> etc. será comentado e se tornará um comentário em vez de um elemento li.

Miriam Salzer
fonte
A pergunta diz "Quero ter uma caixa de mensagem no lugar em que a lista está". Eu também acho que é desvantajoso separar a lógica na s stylesheet. Difícil de manter e pedindo problemas.
Prinzhorn 10/01
11
@Prinzhorn, acho que usar CSS é uma vantagem, porque a lógica é muito simples e fácil de manter, o CSS é reutilizável para outras listas e não depende de JavaScript. Não são necessários ouvintes ou observadores adicionais. A mensagem podia ser estilizada para parecer uma caixa, mas não era para manter a resposta simples.
Miriam Salzer
Alguns meses atrasado, certo, mas eu concordo com Miriam, acho que essa resposta é genial.
Jon Combe
2

Você pode 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
fonte
1

Você pode usar a aspalavra-chave para referenciar uma coleção em um 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
fonte
0

Eu costumo usar o ng-show

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

onde variável você define, por exemplo

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

você pode usar ng-if porque isso não é renderizado na página html e você não vê sua tag html na inspeção ...

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