Использование шаблонов значительно упрощает создание 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>
`
)
Самостоятельная работа
- Сделайте макет сайта
- Создайте в нем верхнее меню, которое будет содержать заголовок и ссылку на главную страницу сайта
- Добавьте футер со ссылкой на ваш профиль в GitHub
- Реорганизуйте шаблоны в вашем приложении так, чтобы они использовали макет
- Попробуйте вносить различные изменения в макет и проверьте, что изменения отражаются сразу на всех страницах приложения. Так вы на практике убедитесь в преимуществе макетов
- Отправьте изменения на GitHub
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.