AngularJS ng-repeat обрабатывает пустой список

377

Я думал, что это будет очень распространенная вещь, но я не мог найти, как справиться с этим в AngularJS. Допустим, у меня есть список событий и я хочу вывести их с AngularJS, тогда это довольно просто:

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

Но как мне справиться со случаем, когда список пуст? Я хочу, чтобы на месте сообщения находилось что-то вроде «Нет событий» или что-то подобное. Единственное, что может подойти ближе - это ng-switchwith events.length(как я могу проверить, пустой ли объект, а не массив?), Но действительно ли это единственный вариант, который у меня есть?

Prinzhorn
источник
4
@ Артем ответил хорошо (+1). Вот обсуждение группы Google, которое использует фильтр, для справки / сравнения: groups.google.com/d/topic/angular/wR06cN5oVBQ/discussion
Марк Раджкок

Ответы:

569

Вы можете использовать ngShow .

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

Смотрите пример .

Или вы можете использовать ngHide

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

Смотрите пример .

Для объекта вы можете проверить Object.keys .

Артем Андреев
источник
1
Действительно @ArtemAndreev. Когда «events» является пустым массивом, он возвращает true, даже если массив пуст
Rob Juurlink
Я думаю, что есть проблема с Object.keys: jsfiddle.net/J9b5z , как бы вы справились с этим?
Дани
5
nh-show работает в моем случае, но не обновляет состояние, когда я вставляю фильтр, и ничего не возвращается. Также объект появляется при первой загрузке и исчезает, когда процесс повторения выполняется при загрузке страницы.
dvdmn
1
@ Дани попробуйте добавить функцию в ваш контроллер, который выполняет тест. Затем вы можете просто вызвать директиву с помощью ng-hide="hasEvents()".
г-н С
Да, спасибо. Однако я надеялся, что будет более элегантный способ без «загрязнения» контроллера.
Дани
370

И если вы хотите использовать это с отфильтрованным списком, вот хитрый трюк:

<ul>
    <li ng-repeat="item in filteredItems  = (items | filter:keyword)">
        ...
    </li>
</ul>
<div ng-hide="filteredItems.length">No items found</div>
Конрад 'ктосо' Малавский
источник
3
Очень полезный. Опечатка, хотя с "отфильтрованных фрагментов".
Равиши
1
Сладкий! Хотя выражение внутри ng-repeat выглядит странно. Есть ли шанс, что вы могли бы это объяснить? Спасибо!!
MK Safi
7
@MKSafi, он создает новую переменную в вызываемой области filteredItemsи устанавливает ее значение (items | filter:keyword)- другими словами, массив, возвращаемый фильтром
AlexFoxGill
17
ДА! Ниндзя плюс очки! Это избавляет от необходимости оценивать сложный фильтр дважды!
markmarijnissen
2
Кроме того, кажется, есть некоторые ограничения вокруг этого с несколькими фильтрами, например, "face in filteredFaces = faces|filter:{deleted: true} | orderBy:'text'но я согласен со всеми, это невероятный трюк.
Сборщик
29

Возможно, вы захотите проверить директиву angular-ui, ui-if если вы просто хотите удалить ulиз DOM, когда список пуст:

<ul ui-if="!!events.length">
    <li ng-repeat="event in events">{{event.title}}</li>
</ul>
Мортимер
источник
1
Спасибо. Чувствует себя чище, чем просто скрывать это.
Принцхорн
@Mortimer: так в этом случае нам нужен angular-ui?
Шиббир Ахмед
вы можете использовать ng-hideбез angular-ui, но он просто скроет узел, он не удалит его из дерева DOM. С помощью ui-ifдирективы angular-ui он удалит узел DOM. Итак, вам нужно как минимум добавить ui-ifдирективу из кода angular-ui в ваш собственный код.
Мортимер
21
новейший угловой в ng-ifкомплекте!
markmarijnissen
4
Обратите внимание, что ng-ifсоздает новую область, где ng-hideнет. Это может вызвать неожиданное поведение.
Арнольд Дэниелс
29

В более новых версиях angularjs правильный ответ на этот вопрос заключается в использовании 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>

Это решение не будет мерцать, когда список собирается загрузить либо потому, что список должен быть определен и имеет длину 0 для отображения сообщения.

Вот плункер, чтобы показать его в использовании: http://plnkr.co/edit/in7ha1wTlpuVgamiOblS?p=preview

Совет: Вы также можете показать загрузочный текст или счетчик:

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

Это похоже на @Konrad 'ktoso' Malawski, но немного легче запомнить.

Протестировано с Angular 1.4

Бернард
источник
3
Вы имеете в видуng-if='!filteredItems.length'
abrunet
Как вы делаете это с несколькими фильтрами?
Джордаш
@ Джордаш Просто продолжай их обыгрывать item in items | filter: ... | filter: ...
Бернард
Хорошо, дальнейшее уточнение<li ng-if="!filteredItems.length">
Мэтти J
Это замечательно. Я использовал намного более грязный метод, чем раньшеitem in (filteredItems = (items | filter: someFilter))
Фирз
6

Вот другой подход с использованием CSS вместо JavaScript / AngularJS.

CSS:

.emptymsg {
  display: list-item;
}

li + .emptymsg {
  display: none;
}

Разметка:

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

Если список пуст, <li ng-repeat = "item in FilterItems"> и т. Д. Будут закомментированы и станут комментарием вместо элемента li.

Мириам Зальцер
источник
Вопрос говорит: «Я хочу, чтобы окно сообщений было в том месте, где находится список». Я также думаю, что невыгодно разделять логику на таблицу стилей s. Трудно поддерживать и напрашиваться на неприятности.
Принцхорн
1
@Prinzhorn, я думаю, что использование CSS является преимуществом, потому что логика очень проста и легка в обслуживании, CSS многократно используется для других списков и не зависит от JavaScript. Никаких дополнительных слушателей или наблюдателей не требуется. Сообщение может быть оформлено так, чтобы оно выглядело как коробка, я просто не хотел, чтобы ответ был простым.
Мириам Зальцер
Опоздал на несколько месяцев, но я согласен с Мириам, я думаю, что этот ответ гениален.
Джон Комб
2

Вы можете использовать этот нг-переключатель:

<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
источник
1

Вы можете использовать asключевое слово для ссылки на коллекцию под ng-repeatэлементом:

<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

я обычно использую нг-шоу

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

где переменная вы определяете например

<div class="list-group-item" ng-repeat="product in store.products">
   <li ng-show="product.length">show something</li>
</div>
Иезекииль Гарсия
источник
0

Вы можете использовать ng-if, потому что он не отображается на html-странице, и вы не видите свой html-тег в 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>
Pejman
источник