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?

1

10 Answers 10

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Indeed @ArtemAndreev. When "events" is an empty array, it returns true even though the array is empty
I think there's a problem with Object.keys: jsfiddle.net/J9b5z, how would you handle this?
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.
@Dani try adding a function to your controller that performs the test. You can then just invoked the directive with ng-hide="hasEvents()".
Yes, thanks. However, I hoped there would be a more elegant way without "polluting" the controller.
|
371

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>

8 Comments

Very useful. Typo though with "filteredFragments".
Sweet! The expression inside ng-repeat looks strange though. Any chance you could explain it? Thanks!!
@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 filter
YES! Ninja plus points! This saves angular from evaluating a complex filter twice!
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.
|
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>

5 Comments

Thanks. Feels cleaner than just hiding it.
@Mortimer: so in this case do we need angular-ui?
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.
newest angular has ng-if included!
Note that ng-if is creating a new scope, where ng-hide isn't. This might cause unexpected behavior.
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>

Comments

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

5 Comments

You mean ng-if='!filteredItems.length'
How do you do this with multiple filters?
@Jordash Just keep piping them item in items | filter: ... | filter: ...
A nice, further refinement is <li ng-if="!filteredItems.length">
This is great. I have been using a lot dirtyer method before like item in (filteredItems = (items | filter: someFilter))
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.

3 Comments

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.
@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.
A few months late, granted, but I agree with Miriam, I think this answer is genius.
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>

Comments

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>

Comments

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>

Comments

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.