Зарегистрируйтесь, чтобы продолжить обучение

Циклы HTML: Препроцессор Pug

В уроке про условные конструкции был использован объект с данными пользователя Кодовёнка Хекслетовича:

const user = {
  name: 'Кодовёнок',
  surname: 'Хекслетович',
  login: 'hexlet-code',
}

При обращении к полям объекта использовалась схема объект.ключ. Таким способом были получены данные об имени, фамилии и логине пользователя:

-
  const user = {
    name: 'Кодовёнок',
    surname: 'Хекслетович',
    login: 'hexlet-code',
  }

section.user-profile
  p.name
    if user.name && user.surname
      | #{user.name} #{user.surname}
    else
      | #{user.login}

Преобразуем немного задачу и выведем пользователя в рейтинге обучающихся. Добавим количество баллов и все данные в таблицу:

-
  const user = {
    name: 'Кодовёнок',
    surname: 'Хекслетович',
    login: 'hexlet-code',
    scores: 1271
  }

section.container
  h2 Рейтинг пользователей

  table
    thead
      tr
        th Имя
        th Фамилия
        th Логин
        th Баллы
    tbody
      tr
        td= user.name
        td= user.surname
        td= user.login
        td= user.scores

Задача легко решается, если необходимо вывести одного пользователя. А что же делать, если пользователя 2? 3? 100? Есть два способа:

  1. Плохой: записать каждого пользователя в свою переменную и методом «копировать-вставить» добавить всех пользователей.
  2. Хороший: создать массив users, внутри которого будут все пользователи. Таким образом каждому пользователю будет выделено своё место в массиве, а значит можно удобно добавлять или удалять пользователей.

Создадим массив users и добавим туда несколько разных пользователей:

const users = [
  {
    name: 'Кодовёнок',
    surname: 'Хекслетович',
    login: 'hexlet-code',
    scores: 1271
  },
  {
    name: 'Король',
    surname: 'Вёрстки',
    login: 'king-of-layout',
    scores: 1100
  },
]

Для обхода такого массива используются специальные конструкции — циклы. Их задача — пройтись по каждому элементу массива и получить информацию внутри него. Проход по элементам называется итерацией. Во время первой итерации будут получены данные Кодовёнка Хекслетовича. Во время второй итерации данные Короля Вёрстки.

Основным типом цикла в Pug считается цикл each in. Буквально его можно читать как для каждого «a» внутри «b». Где:

  • «a» — произвольное имя переменной, которая будет доступна во время итерации.
  • «b» — массив или объект, из которого необходимо получить данные.
-
  const users = [
    {
      name: 'Кодовёнок',
      surname: 'Хекслетович',
      login: 'hexlet-code',
      scores: 1271
    },
    {
      name: 'Король',
      surname: 'Вёрстки',
      login: 'king-of-layout',
      scores: 1100
    },
  ]

section.container
  h2 Рейтинг пользователей

  table
    thead
      tr
        th Имя
        th Фамилия
        th Логин
        th Баллы
    tbody
      each user in users
        tr
          td= user.name
          td= user.surname
          td= user.login
          td= user.scores
<section class="container">
  <h2>Рейтинг пользователей</h2>
  <table>
    <thead>
      <tr>
        <th>Имя</th>
        <th>Фамилия</th>
        <th>Логин</th>
        <th>Баллы</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Кодовёнок</td>
        <td>Хекслетович</td>
        <td>hexlet-code</td>
        <td>1271</td>
      </tr>
      <tr>
        <td>Король</td>
        <td>Вёрстки</td>
        <td>king-of-layout</td>
        <td>1100</td>
      </tr>
    </tbody>
  </table>
</section>

Главное изменение произошло добавлением всего одной строки: each user in users. Запись читается следующим образом: для каждого user в массиве users и далее вывод табличной строки с данными.


Важно: вы можете присвоить любое имя переменной, которая будет доступна внутри цикла. В прошлом примере таким именем может быть не user, а people или как-то иначе. Только вы выбираете это имя, но старайтесь называть его так, чтобы было понятно, что кроется за этим названием. Если код внутри цикла большой, то именно от именования зависит то, как быстро и точно будет интерпретирован код вами или другим разработчиком.

Подробнее можно почитать в статьях:


Нельзя точно быть уверенным, что массив, по которому будет происходить перебор имеет хоть один элемент. Если нет ни одного элемента, то всё, что внутри массива не будет выведено. В этот момент пользователю важно знать, что информации нет, а не возникла ошибка на странице. Для этого можно использовать конструкцию each else. Это схоже с условной конструкцией, но срабатывает только при пустом массиве.

- const users = []

section.container
  h2 Рейтинг пользователей

  table
    thead
      tr
        th Имя
        th Фамилия
        th Логин
        th Баллы
    tbody
      each user in users
        tr
          td= user.name
          td= user.surname
          td= user.login
          td= user.scores
      else
        tr
          td(colspan='4') Нет пользователей
<section class="container">
  <h2>Рейтинг пользователей</h2>
  <table>
    <thead>
      <tr>
        <th>Имя</th>
        <th>Фамилия</th>
        <th>Логин</th>
        <th>Баллы</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td colspan="4">Нет пользователей</td>
      </tr>
    </tbody>
  </table>
</section>

Код из прошлого примера эквивалентен следующей записи:

- const users = []

if users.length
  each user in users
    tr
      td= user.name
      td= user.surname
      td= user.login
      td= user.scores
else
  tr
    td(colspan='4') Нет пользователей

Вложенные циклы и получение ключа объекта

При переборе объекта бывает необходимо получить не только значение, но и ключ, по которому располагается объект. Например, пользователи могут делиться на разные группы: пользователи, модераторы, администраторы. В таком случае объект может иметь следующий вид:

const users = {
  admin: [
    {
      name: 'Кодовёнок',
      surname: 'Хекслетович',
    },
  ],
  moderator: [
    {
      name: 'Король',
      surname: 'Вёрстки',
    },
    {
      name: 'Алексей',
      surname: 'Примадонин',
    },
  ],
}

Объект users имеет несколько ключей: admin и moderator, значением которых являются массивы пользователей. Для вывода такого списка вместе с указанием должности используется немного изменённый синтаксис цикла each. В нём необходимо получить имя ключа и массив.

each people, position in users
  h2= position
  ul
    each user in people
      li= user.name + ' ' + user.surname

Из объекта users принимаются несколько данных: в переменную people попадает значение, а в переменную position ключ. Обратите внимание на порядок переменных — вначале данные, а потом ключ.

В этом примере используется вложенный цикл. Такая практика является стандартным для обхода больших данных. При первой итерации в верхнем цикле вытаскиваются следующие данные:

const people = [
  {
    name: 'Кодовёнок',
    surname: 'Хекслетович',
  },
];

const position = 'admin';

В дальнейшем идет перебор по массиву people, который происходит так же, как и в примере выше.

<h2>admin</h2>
<ul>
  <li>Кодовёнок Хекслетович</li>
</ul>
<h2>moderator</h2>
<ul>
  <li>Король Вёрстки</li>
  <li>Алексей Примадонин</li>
</ul>

Цикл while

Цикл each по праву считается основным при работе с Pug, но он не является единственным. Встречаются ситуации, когда необходимо несколько раз повторить одни и те же действия. В этом случае each никак не поможет, но в Pug существует ещё один тип цикла — while. Его задача — повторение участка кода пока истинно условие. Например,

- let count = 0;

ul
  while count < 5
    li= "Hello, my number is " + count
    - count += 1;
<ul>
  <li>Hello, my number is 0</li>
  <li>Hello, my number is 1</li>
  <li>Hello, my number is 2</li>
  <li>Hello, my number is 3</li>
  <li>Hello, my number is 4</li>
</ul>

При использовании цикла while важно следить за тем, чтобы условие рано или поздно преобразовалось в ложь. Это частая ошибка, которая может привести к бесконечному циклу. В прошлом примере для получения бесконечного цикла достаточно опустить строку count += 1;. Без неё значение переменной count всегда будет равно нулю, а значит условие count < 5 будет истинным в любой момент времени компиляции.

Дополнительное задание

В файл icon.pug попадает массив следующего вида:

-
  const icons = {
    free: [
      {
        name: 'robot',
        url: './icons/robot.svg'
      },
      {
        name: 'hexlet',
        url: './icons/hexlet.svg'
      }
    ],
    premium: [
      {
        name: 'cat',
        url: './icons/premium/cat.svg'
      },
      {
        name: 'dog',
        url: './icons/premium/dog.svg'
      }
    ]
  };

Преобразуйте данные в шаблон следующего вида:

<h2>free</h2>
<ul>
  <li><a href="./icons/robot.svg">robot</a></li>
  <li><a href="./icons/hexlet.svg">hexlet</a></li>
</ul>
<h2>premium</h2>
<ul>
  <li><a href="./icons/premium/cat.svg">cat</a></li>
  <li><a href="./icons/premium/dog.svg">dog</a></li>
</ul>

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»