Register to get access to free programming courses with interactive exercises

Cycles HTML: Preprocessor Pug

In the lesson about conditional constructs, we used an object with data belonging to a user called Hexlet McCoderson:

const user = {
  name: 'Hexlet',
  surname: 'McCoderson',
  login: 'hexlet-code',
}

We used object.key when accessing the object fields to get data on the name and username of the user:

-
  const user = {
    name: 'Hexlet',
    surname: 'McCoderson',
    login: 'hexlet-code',
  }

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

Let us modify the task and add the number of points and all the data to the table. It helps to display the user in the learner rankings:

-
  const user = {
    name: 'Hexlet',
    surname: 'McCoderson',
    login: 'hexlet-code',
    scores: 1271
  }

section.container
  h2 User rating

  table
    thead
      tr
        th Name
        th Surname
        th Login
        th Scores
    tbody
      tr
        td= user.name
        td= user.surname
        td= user.login
        td= user.scores

This task is easy if you need to output a single user. But what if there are hundreds of users? There are two ways:

  1. Bad: Write each user to their variable and add all users with the classic copy-and-paste method
  2. Good: create an array of users with all users inside it. Every user will be allocated their place in the array, making it convenient to add or delete users
const users = [
  {
    name: 'Hexlet',
    surname: 'McCoderson',
    login: 'hexlet-code',
    scores: 1271
  },
  {
    name: 'Layout',
    surname: 'ODesign',
    login: 'king-of-layout',
    scores: 1100
  },
]

We can use constructs called loops to traverse such an array. They go through each array element and get the information inside it.

Passing through the elements is called iteration. In the first iteration, you'll find Hexlet McCoderson's data. In the second iteration, you'll get Layout O'Design's data.

The main type of loop in Pug is the each in, meaning for every an inside b:

  • a is an arbitrary variable name available during iteration
  • b is the array or object you want to retrieve the data
-
  const users = [
    {
      name: 'Hexlet',
      surname: 'McCoderson',
      login: 'hexlet-code',
      scores: 1271
    },
    {
      name: 'Layout',
      surname: 'ODesign',
      login: 'king-of-layout',
      scores: 1100
    },
  ]

section.container
  h2 User rating

  table
    thead
      tr
        th Name
        th Surname
        th Login
        th Scores
    tbody
      each user in users
        tr
          td= user.name
          td= user.surname
          td= user.login
          td= user.scores
<section class="container">
  <h2>User rating</h2>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Surname</th>
        <th>Login</th>
        <th>Scores</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Hexlet</td>
        <td>McCoderson</td>
        <td>hexlet-code</td>
        <td>1271</td>
      </tr>
      <tr>
        <td>Layout</td>
        <td>ODesign</td>
        <td>king-of-layout</td>
        <td>1100</td>
      </tr>
    </tbody>
  </table>
</section>

We changed one line: each user in users. We read it as follows: for each user in the user's array and then output a tabular string with the data.

You can assign any name to the variable that will be accessible inside the loop. The name might not be user, it could be people or something else. You can choose any name that accurately represents the content.

If the code inside the loop is large, the naming determines how quickly and accurately the code will be interpreted by you or another developer. You can read more in these articles:

You can't be sure the array we search has at least one element. The array's content won't be output If there are no elements.

At this point, the user needs to know that there is no data, so they don't think there is an error with the page. It is what the `each else' is for. It is similar to the conditional construct but only works if the array is empty:

- const users = []

section.container
  h2 User rating

  table
    thead
      tr
        th Name
        th Surname
        th Login
        th Scores
    tbody
      each user in users
        tr
          td= user.name
          td= user.surname
          td= user.login
          td= user.scores
      else
        tr
          td(colspan='4') No users
<section class="container">
  <h2>User rating</h2>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Surname</th>
        <th>Login</th>
        <th>Scores</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td colspan="4">No users</td>
      </tr>
    </tbody>
  </table>
</section>

The code from the last example is equivalent to the following:

- 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') No users

Nested loops and getting the object key

When going through an object, you sometimes have to get the value and the key in the object's location. For example, we can divide users into different groups: users, moderators, administrators, and so on. The object may have the following form in this case:

const users = {
  admin: [
    {
      name: 'Hexlet',
      surname: 'McCoderson',
    },
  ],
  moderator: [
    {
      name: 'Layout',
      surname: 'ODesign',
    },
    {
      name: 'Alex',
      surname: 'Bossman',
    },
  ],
}

The users have several admin and moderator keys, which values are arrays of users. We can use a slightly modified each loop syntax to output this list with job titles. We need to get the key name and the array in it:

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

We take several pieces of data from the users object: the people variable gets the value, and the position variable gets the key. Pay attention to the order of the variables: the data first and then the key.

This example uses a nested loop which is standard for traversing big data. The first iteration in the top loop pulls the following data:

const people = [
  {
    name: 'Hexlet',
    surname: 'McCoderson',
  },
];

const position = 'admin';

The next step is to search through the people array, which we do in the same way as in the example above:

<h2>admin</h2>
<ul>
  <li>Hexlet McCoderson</li>
</ul>
<h2>moderator</h2>
<ul>
  <li>Layout ODesign</li>
  <li>Alex Bossman</li>
</ul>

While loop

When we work with Pug, we consider the each cycle the main one, but it isn't the only one.

There are situations when we repeat the same actions several times. In this case, each doesn't help, but there is another loop type – while. It repeats a code section while the condition is true. For example:

- 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>

When we use the while, we should check that the condition changes to false sooner or later. It is a common mistake that can lead to an endless loop. In the last example, you should use count += 1; to get an infinite loop. Without it, the value of count will always be zero, meaning that the condition count < 5 will be true at any point during compilation.

Additional assignment

The following kind of array goes into the 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'
      }
    ]
  };

Convert the data into the following template:

<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>

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs
profession
Layout with the latest CSS standards
5 months
from scratch
under development
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.