Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Лейауты в JTE Java: Веб-технологии

Использование шаблонов значительно упрощает создание HTML-страниц на стороне сервера. Но у этого подхода есть и недостатки. Он порождает определенные неудобства, связанные с повторяющимися блоками. В этом уроке мы поговорим об этой проблеме и выясним, как предотвращать такие ситуации.

Инвертированный подход к повторяющимся блокам

Представьте себе типовой сайт. На большинстве сайтов есть верхнее меню, футер и еще немало повторяющихся блоков. Как с ними правильно работать? Кажется, что копировать все это в каждый шаблон — не самая лучшая идея.

Есть еще один подход — вынести общие куски в отдельные файлы и включить их в нужных шаблонах:

<!-- Пример шаблонизатора JSP -->
<%@ include file="parts/header.jsp" %>
<!-- Здесь код конкретного шаблона -->
<%@ include file="parts/footer.jsp" %>

Это простое решение, которые использовалось в самых ранних шаблонизаторах. Разработчики быстро поняли, насколько это неудобно. Проблема заключается в двух аспектах:

  • Разработчику приходится делать подобные включения абсолютно в каждом шаблоне вручную. В нашем примере их всего два. А что, если бы их были сотни? На работу с каждым отдельным шаблоном ушло бы очень много времени
  • Разработчику приходится вручную проверять разные файлы и искать незакрытые теги, потому что части одних и тех же тегов могут быть разбросаны по разным файлам. Отлаживать такие проекты очень сложно

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

  • В одном месте описываем макет – основу сайта, которая не меняется по структуре
  • Шаблонизатор делает так, чтобы шаблон конкретной страницы вставлялся в этот макет

В этом случае шаблон ничего не знает об устройстве макета, а макет хранит все свои части в одном месте.

Инвертированный подход в JTE

Шаблонизатор JTE поддерживает работу с макетами. Посмотрим, как это работает. Для начала создадим макет src/main/jte/layout/page.jte:

@import gg.jte.Content
@param Content content

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Hexlet Javalin Example</title>
    </head>
    <body>
        <p>
            <a href="/users">Users</a>
            <a href="/courses">Courses</a>
        </p>
        ${content}
    </body>
</html>

Обратим внимание на ${content} — сюда JTE поместит шаблон конкретной страницы. Посмотрим, как выглядит шаблон списка пользователей:

@import org.example.hexlet.dto.UsersPage
@param UsersPage page

@template.layout.page(
    content = @`
        <a href="/users/build">New User</a>
        @for(var user : page.getUsers())
            <div>${user.getName()}</div>
        @endfor
    `
)

Главное отличие здесь в том, что мы помещаем содержимое шаблона в специальную конструкцию, которая вставляет его в нужное место в макет:

@template.layout.page(content = @``)

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

@import org.example.Page
@import gg.jte.Content

@param Page page
@param Content content
@param Content footer = null

<head>
    @if(page.getDescription() != null)
        <meta name="description" content="${page.getDescription()}">
    @endif
    <title>${page.getTitle()}</title>
</head>
<body>
    <h1>${page.getTitle()}</h1>
    <div class="content">
        ${content}
    </div>
    @if (footer != null)
        <div class="footer">
            ${footer}
        </div>
    @endif
</body>

Кроме content, в этом макете используется footer и page, которые придут из конкретного шаблона. По умолчанию footer равен null, поэтому мы можем его и не передавать. Соответствующий блок будет скрыт:

@import org.example.WelcomePage
@param WelcomePage welcomePage

@template.layout.page(
    page = welcomePage,
    content = @`
        <p>Welcome, ${welcomePage.getUserName()}.</p>
    `,
    footer = @`
        <p>Thanks for visiting, come again soon!</p>
    `
)

Самостоятельная работа

  1. Сделайте макет сайта
  2. Создайте в нем верхнее меню, которое будет содержать заголовок и ссылку на главную страницу сайта
  3. Добавьте футер со ссылкой на ваш профиль в GitHub
  4. Реорганизуйте шаблоны в вашем приложении так, чтобы они использовали макет
  5. Попробуйте вносить различные изменения в макет и проверьте, что изменения отражаются сразу на всех страницах приложения. Так вы на практике убедитесь в преимуществе макетов
  6. Отправьте изменения на GitHub

Дополнительные материалы

  1. Официальная документация jte

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

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

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

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

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 21 ноября

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

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

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

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