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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Prinzhorn
  • 22,120
  • 7
  • 61
  • 65
  • 4
    @Artem's answer is good (+1). Here's a google group discussion that uses a filter, for reference/comparison: https://groups.google.com/d/topic/angular/wR06cN5oVBQ/discussion – Mark Rajcok Sep 10 '12 at 15:49

10 Answers10

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.

Community
  • 1
  • 1
Artem Andreev
  • 19,942
  • 5
  • 43
  • 42
  • 1
    Indeed @ArtemAndreev. When "events" is an empty array, it returns true even though the array is empty – Rob Juurlink May 15 '13 at 12:33
  • I think there's a problem with Object.keys: http://jsfiddle.net/J9b5z/, how would you handle this? – Dani Jun 10 '13 at 20:34
  • 5
    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. – dvdmn Jun 13 '13 at 12:54
  • 1
    @Dani try adding a function to your controller that performs the test. You can then just invoked the directive with `ng-hide="hasEvents()"`. – Mr. S Aug 23 '13 at 20:23
  • Yes, thanks. However, I hoped there would be a more elegant way without "polluting" the controller. – Dani Aug 27 '13 at 14:05
  • What if I use loads of filter in ng-repeat? – tom10271 Aug 24 '15 at 10:02
  • 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. – Ingadi Jun 06 '16 at 18:16
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>
Jim Buck
  • 2,383
  • 23
  • 42
Konrad 'ktoso' Malawski
  • 13,102
  • 3
  • 47
  • 52
  • 3
    Very useful. Typo though with "filteredFragments". – ravishi Sep 04 '13 at 20:26
  • 1
    Sweet! The expression inside ng-repeat looks strange though. Any chance you could explain it? Thanks!! – M.K. Safi Dec 12 '13 at 14:03
  • 7
    @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 – Alex Jan 14 '14 at 16:26
  • 17
    YES! Ninja plus points! This saves angular from evaluating a complex filter twice! – markmarijnissen Mar 19 '14 at 19:40
  • 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? – colllin Nov 02 '14 at 18:35
  • 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. – Fitter Man Feb 17 '15 at 23:01
  • 2
    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. – Fitter Man Feb 17 '15 at 23:12
  • 1
    Nice trick, but I'm curious why the whole
      instead of the
    • -Element is repeated here?
    – Mitja Jul 21 '15 at 13:49
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>
Mortimer
  • 2,966
  • 23
  • 24
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>
cameck
  • 2,058
  • 20
  • 32
Pylinux
  • 11,278
  • 4
  • 60
  • 67
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

Bernard
  • 16,149
  • 12
  • 63
  • 66
  • This is great. I have been using a lot dirtyer method before like `item in (filteredItems = (items | filter: someFilter))` – Firze Aug 25 '17 at 08:38