Рекурсивные шаблоны в AngularJS

Предположим, что у нас есть иерархический набор данных, например такой как список категорий для продуктов в интернет магазине. Как мы можем использовать AngularJS для отображения вложенных категорий независимо от их глубины вложенности?

Ответ на этот вопрос достаточно прост - рекурсивные шаблоны, а для их реализации мы будем использовать директиву ng-include.

Взгляните на следующий код:

$scope.categories = [
  { 
    title: 'Компьютеры',
    categories: [
      {
        title: 'Ноутбуки',
        categories: [
          {
            title: 'Ультрабуки'
          },
          {
            title: 'Макбуки'            
          }
        ]
      },
      {
        title: 'Настольные компьютеры'
      },
      {
        title: 'Планшеты',
        categories: [
          { 
            title: 'Apple'
          },
          {
            title: 'Android'
          }
        ]        
      }
    ]
  },
  {
    title: 'Принтеры'
  }
];

Наша цель получить вложенный список такого вида:

  • Компьютеры
    • Ноутбуки
      • Ультрабуки
      • Макбуки
    • Настольные компьютеры
    • Планшеты
      • Apple
      • Android
  • Принтеры

Начнём с отображения категорий верхнего уровня:

<ul>
    <li ng-repeat="category in categories">{{ category.title }}</li>
</ul>

Получим следующий список:

  • Компьютеры
  • Принтеры

Если мы хотим отобразить следующий уровень вложенности для категорий, необходимо немного обновить html:

<ul>
    <li ng-repeat="category in categories">
        {{ category.title }}
        <ul ng-if="category.categories">
            <li ng-repeat="category in category.categories">
                {{ category.title }}
            </li>
        </ul>
    </li>
</ul>

Сейчас мы получим следущий список:

  • Компьютеры
    • Ноутбуки
    • Настольные компьютеры
    • Планшеты
  • Принтеры

Мы могли бы продолжить дублирование кода для каждой категории, но такой подход очень сложно поддерживать в будущем, также в данном случае нам необходимо заранее знать уровень вложенности категорий, но мы хотим этого избежать.

Гораздо лучшее решение - это использовать шаблон и рекурсивно рендерить его для каждой вложенной категории. Давайте создадим этот шаблон:

<script type="text/ng-template" id="category-tree">
    {{ category.title }}
    <ul ng-if="category.categories">
        <li ng-repeat="category in category.categories" ng-include="'category-tree'">           
        </li>
    </ul>
</script>

Для каждой категории мы отображаем её заголовок, а также список её вложенных категорий посредством включения шаблона с помощью директивы ng-include.

В завершении, удалим тот код, что мы создали и добавим подключение шаблона в корневые категории:

<ul>
    <li ng-repeat="category in categories" ng-include="'category-tree'"></li>
</ul> 

Как это работает?

ng-include для каждой итерации, создаёт новую дочерную область видимости (scope). Переменная category автоматически становится видимой внутри шаблона и относится только к текущей итерации. Рекурсивный шаблон работает, потому что мы используем одно и тоже имя для переменной категории (category), как внутри шаблона так и снаружи. Если вы захотите использовать другое имя для категории, то используйте директиву ng-init:

<li ng-repeat="parentCategory in categories" 
    ng-include="'category-tree'" 
    ng-init="category=parentCategory"></li> 

Комментарии

0