AngularJS ng-repeat handle empty list case
Asked Answered
R

10

378

I thought this would be a very common thing, but I couldn't find how to handle it in AngularJS. Let's say I have a list of events and want to output them with AngularJS, then that's pretty easy:

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

But how do I handle the case when the list is empty? I want to have a message box in place where the list is with something like "No events" or similar. The only thing that would come close is the ng-switch with events.length (how do I check if empty when an object and not an array?), but is that really the only option I have?

Rudolph answered 9/9, 2012 at 14:52 Comment(1)
@Artem's answer is good (+1). Here's a google group discussion that uses a filter, for reference/comparison: groups.google.com/d/topic/angular/wR06cN5oVBQ/discussionExpatriate
L
571

You can use ngShow.

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

See example.

Or you can use ngHide

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

See example.

For object you can test Object.keys.

Luigi answered 9/9, 2012 at 17:19 Comment(7)
Indeed @ArtemAndreev. When "events" is an empty array, it returns true even though the array is emptyWanderjahr
I think there's a problem with Object.keys: jsfiddle.net/J9b5z, how would you handle this?Kerk
nh-show worked id my case but it doesnt update the state when I put a filter in and nothing returned. Also the object appears on first load and disappears when the repeat process done on page load.Gaytan
@Kerk try adding a function to your controller that performs the test. You can then just invoked the directive with ng-hide="hasEvents()".Ro
Yes, thanks. However, I hoped there would be a more elegant way without "polluting" the controller.Kerk
What if I use loads of filter in ng-repeat?Lumpen
As @RobJuurlink pointed out this won't work as expected. I think ng-show="events.length < 1" or ng-hide="events.length > 1" would be a better solution.Lollard
P
372

And if you want to use this with a filtered list here's a neat trick:

<ul>
    <li ng-repeat="item in filteredItems  = (items | filter:keyword)">
        ...
    </li>
</ul>
<div ng-hide="filteredItems.length">No items found</div>
Paton answered 3/9, 2013 at 13:0 Comment(8)
Very useful. Typo though with "filteredFragments".Madeline
Sweet! The expression inside ng-repeat looks strange though. Any chance you could explain it? Thanks!!Questionnaire
@MKSafi, it is creating a new variable on the scope called filteredItems and setting its value to (items | filter:keyword) - in other words, the array returned by the filterNeysa
YES! Ninja plus points! This saves angular from evaluating a complex filter twice!Treenware
This duplicates the filtered list though, right? Could cause slowness depending on how big your list is and what's in it. Or does Angular already save the filtered list in memory, so now you're just saving a reference to it in the scope so you can access it later?Sihon
What's the scope of the variable that's created? I'd like to use it to disable a div that contains the list. The contain div doesn't seem to be able to access the variable.Embarrass
Also, there seem to be some limitations around this with multiple filters, like "face in filteredFaces = faces|filter:{deleted: true} | orderBy:'text' but I agree with everyone, this is a fabulous trick.Embarrass
Nice trick, but I'm curious why the whole <ul> instead of the <li>-Element is repeated here?Hodge
W
29

You might want to check out the angular-ui directive ui-if if you just want to remove the ul from the DOM when the list is empty:

<ul ui-if="!!events.length">
    <li ng-repeat="event in events">{{event.title}}</li>
</ul>
Wandering answered 1/12, 2012 at 21:40 Comment(5)
Thanks. Feels cleaner than just hiding it.Rudolph
@Mortimer: so in this case do we need angular-ui?Broadminded
you can use ng-hide without angular-ui, but it will just hide the node, it won't remove it from the DOM tree. With angular-ui's ui-if directive, it will remove the DOM node. So, you need to at least add the ui-if directive from the angular-ui code to your own code.Wandering
newest angular has ng-if included!Treenware
Note that ng-if is creating a new scope, where ng-hide isn't. This might cause unexpected behavior.Eckard
G
29

With the newer versions of angularjs the correct answer to this question is to use 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>

This solution will not flicker when the list is about to download either because the list has to be defined and with a length of 0 for the message to display.

Here is a plunker to show it in use: http://plnkr.co/edit/in7ha1wTlpuVgamiOblS?p=preview

Tip: You can also show a loading text or spinner:

  <li ng-if="!list">( Loading... )</li>
Grice answered 2/1, 2015 at 7:49 Comment(0)
D
23
<ul>
    <li ng-repeat="item in items | filter:keyword as filteredItems">
        ...
    </li>
    <li ng-if="filteredItems.length===0">
        No items found
    </li>
</ul>

This is similar to @Konrad 'ktoso' Malawski but slightly easier to remember.

Tested with Angular 1.4

Downatheel answered 6/10, 2015 at 1:14 Comment(5)
You mean ng-if='!filteredItems.length'Disappointment
How do you do this with multiple filters?President
@President Just keep piping them item in items | filter: ... | filter: ...Downatheel
A nice, further refinement is <li ng-if="!filteredItems.length">Lelalelah
This is great. I have been using a lot dirtyer method before like item in (filteredItems = (items | filter: someFilter))Occupy
F
6

Here's a different approach using CSS instead of JavaScript/AngularJS.

CSS:

.emptymsg {
  display: list-item;
}

li + .emptymsg {
  display: none;
}

Markup:

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

If the list is empty, <li ng-repeat="item in filteredItems">, etc. will get commented out and will become a comment instead of a li element.

Foozle answered 9/1, 2016 at 4:57 Comment(3)
Question says "I want to have a message box in place where the list is". I also think it's disadvantageous to separate the logic into s stylesheet. Hard to maintain and asking for trouble.Rudolph
@Prinzhorn, I think using CSS is an advantage because the logic is very simple and easy to maintain, the CSS is reusable for other lists, and it doesn't rely on JavaScript. No additional listeners or watchers are needed. The message could be styled to look like a box, I just didn't to keep the answer simple.Foozle
A few months late, granted, but I agree with Miriam, I think this answer is genius.Libreville
C
2

You can use this 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>
Caesarean answered 9/10, 2015 at 13:35 Comment(0)
S
2

You can use as keyword to refer a collection under a ng-repeat element:

<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>
Suckow answered 22/2, 2019 at 21:33 Comment(0)
I
0

i usually use ng-show

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

where variable you define for example

<div class="list-group-item" ng-repeat="product in store.products">
   <li ng-show="product.length">show something</li>
</div>
Ipomoea answered 20/3, 2016 at 3:33 Comment(0)
Y
0

you can use ng-if because this is not render in html page and you dont see your html tag in 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>
Yester answered 25/12, 2016 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.