Оглавление
Первая задача — создать домашнюю страницу для нашего продуктового сайта.
Первая версия страницы будет предельно проста: просто заголовок и приветственное сообщение. Это наш /WEB-INF/templates/home.html файл:
Первое, что вы заметите, это то, что этот файл стандарта HTML5, который может быть правильно отображен любым браузером, потому что он не содержит теги, отличные от HTML (браузеры игнорируют все атрибуты, которые они не понимают, например «th:text»).
Но вы также можете заметить, что этот шаблон не является действительно допустимым документом HTML5, потому что нестандартные атрибуты, которые мы используем в форме «th: *», не допускаются спецификацией HTML5. На самом деле, мы даже добавляем атрибут «xmlns: th» в наш тег , что абсолютно не-HTML5-но:
… который вообще не влияет на обработку шаблонов, но работает как «заклинание», которое мешает нашей среде IDE жаловаться на отсутствие определения пространства имен для всех этих атрибутов th: *.
Как сделать этот шаблон HTML5? Легко: переключитесь на синтаксис атрибутов Thymeleaf, используя "data-" для имен атрибутов и дефис (-) вместо двоеточия (:):
Пользовательские аттрибуты с префиксом «data-» допускаются спецификацией HTML5, поэтому, используя этот код выше, наш шаблон будет действительным документом HTML5.
Оба обозначения полностью эквивалентны и взаимозаменяемы, но для простоты и компактности примеров кода этот учебник будет использовать нотацию пространства имен «th: *». Кроме того, нотация «th: *» является более общей и разрешена в каждом типе шаблона Thymeleaf (XML, TEXT ...), тогда как префикс аттрибутов «data-» разрешается только в режиме HTML.
Использование th:text и многоязычность текста
Многоязычный текст представлен вне файлов шаблонов и хранится в отдельных файлах (обычно в файлах .properties) и его можно легко заменить эквивалентными текстами, написанными на других языках (процесс, называемый интернационализацией или просто i18n). Такие «внешние фрагменты текста» обычно называются «сообщениями».
В сообщениях всегда есть ключ, который идентифицирует их, а Thymeleaf позволяет указать, что текст должен соответствовать конкретному сообщению с синтаксисом #{...}:
Мы можем видеть две особенности Стандартного диалекта Thymeleaf:
Аттрибут "th:text", который обрабатывает собственное значение и устанавливает результат обработки в теле связанного тега, перезаписывая «Welcome to our grocery store!» текст.
Выражение "#{home.welcome}", определяемое Стандартным синтаксисом Выражение, указывая, что текст внутри аттрибута "th:text" должен быть сообщением с ключом «home.welcome», связанным с локалью.
И где здесь многоязычность ?
Место расположения многоязычного текста в Thymeleaf полностью конфигурируемое и определяется реализацией интерфейса org.thymeleaf.messageresolver.IMessageResolver. Обычно, реализация основана на использовании файлов .properties, но мы можем создать нашу собственную реализацию, если захотим, для извлечения сообщений из Базы данных.
В примере мы не определяем message resolver в течение инициализации, что означает — наше приложение использует Standard Message Resolver, реализованный в org.thymeleaf.messageresolver.StandardMessageResolver.
Standard message resolver ищет распложение файлов сообщений /WEB-INF/templates/home.html в файлах «properties» в той же директории с тем же именем, как наш шаблон — пример:
/WEB-INF/templates/home_en.properties для English текста.
/WEB-INF/templates/home_es.properties для Spanish текста.
/WEB-INF/templates/home_pt_BR.properties для Portuguese (Brazil) текста.
/WEB-INF/templates/home.properties для текста по умолчанию (если локаль не определена).
Давайте посмотрим в наш home_es.properties файл:
Это все, что нам нужно для того, чтобы заставить Thymeleaf обработать шаблон. Давайте теперь создадим Home контроллер.
Контексты/Contexts
Чтобы обработать наш шаблон, мы создадим класс HomeController, реализующий интерфейс IGTVGController, который мы видели ранее:
Первое, что мы видим, это создание контекста. Контекст Thymeleaf — это объект, реализующий интерфейс org.thymeleaf.context.IContext. Контексты должны содержать все данные, необходимые для выполнения механизма шаблона в Map переменных, а также ссылаться на языковой стандарт, который должен использоваться для сообщений.
Существует специализированное расширение этого интерфейса org.thymeleaf.context.IWebContext, предназначенное для использования в веб-приложениях на основе ServletAPI (например, SpringMVC).
В основной библиотеке Thymeleaf представлена реализация каждого из этих интерфейсов:
org.thymeleaf.context.Context реализует IContext
org.thymeleaf.context.WebContext реализует IWebContext
И, как вы можете видеть в коде контроллера, WebContext — это тот, который мы используем. На самом деле это необходимо, потому что использование ServletContextTemplateResolver требует использования контекста, реализующего IWebContext.
Требуется только три из этих четырех аргументов конструктора, потому что будет использоваться дефолтная локаль для системы, если ни одна не указана (хотя вы никогда не должны допускать, чтобы это происходило в реальных приложениях).
Существуют некоторые специализированные выражения, которые мы сможем использовать для получения параметров запроса и атрибутов запроса, сеанса и приложения из WebContext в наших шаблонах. Например:
${x} вернет переменную x, сохраненную в контексте Thymeleaf, или как атрибут запроса.
${param.x} вернет параметр запроса с именем x (который может быть многозначным).
${session.x} вернет атрибут сессии с именем x.
${application.x} вернет атрибут контекста сервлета с именем x.
Запуск template engine
Когда наш объект контекста готов, мы можем сообщить движку шаблона обработать шаблон (по его названию) с использованием контекста и передать ответ, чтобы ответ мог быть внедрен в шаблон:
Посмотрим результат с использование локали Spanish:
Экранированный текст
Простейшая версия нашей домашней страницы, похоже, сейчас готова, но есть кое-что, о чем мы не думали… что, если бы у нас было такое сообщение?
Если мы выполним этот шаблон, как и раньше, мы получим:
Это не совсем то, что мы ожидали, потому что наш тег <b> был экранирован, и поэтому он будет отображаться в браузере.
Это поведение по умолчанию атрибута "th:text". Если мы хотим, чтобы Thymeleaf не менял наши теги HTML и не избегал их, нам придется использовать другой атрибут: th:utext («unescaped text»):
Это выводит наше сообщение так, как мы этого хотели:
Использование и отображение переменных
Теперь добавим еще немного контента на нашу домашнюю страницу. Например, мы можем отображать дату ниже нашего приветственного сообщения:
Прежде всего, нам придется изменить наш контроллер, чтобы добавить эту дату в качестве переменной контекста:
Мы добавили переменную String, названную "today" в наш контекст, и теперь можем отобразить ее в нашем шаблоне:
Как вы можете видеть, мы все еще используем атрибут "th:text" для работы (и это правильно, потому что мы хотим заменить тело тега), но синтаксис на этот раз немного отличается, а вместо #{...}, мы используем ${...}. Это выражение с переменной и содержит выражение на языке OGNL (Object-Graph Navigation Language), который будет выполняться на контекстных переменных в map, о которой мы говорили ранее.
Выражение ${today} просто означает «получить переменную, называемую today», но эти выражения могут быть более сложными (например, ${user.name} для «получить переменную, называемую user, и вызвать ее метод getName()»).
В значениях атрибутов имеется довольно много возможностей: сообщения, переменные… и многое другое. Следующая глава покажет нам, каковы все эти возможности.
Продолжение. Глава 4. Standard Expression Syntax
3 Использование Text
3.1 Мультиязычное «Добро пожаловать»
Первая задача — создать домашнюю страницу для нашего продуктового сайта.
Первая версия страницы будет предельно проста: просто заголовок и приветственное сообщение. Это наш /WEB-INF/templates/home.html файл:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>
<body>
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
</body>
</html>
Первое, что вы заметите, это то, что этот файл стандарта HTML5, который может быть правильно отображен любым браузером, потому что он не содержит теги, отличные от HTML (браузеры игнорируют все атрибуты, которые они не понимают, например «th:text»).
Но вы также можете заметить, что этот шаблон не является действительно допустимым документом HTML5, потому что нестандартные атрибуты, которые мы используем в форме «th: *», не допускаются спецификацией HTML5. На самом деле, мы даже добавляем атрибут «xmlns: th» в наш тег , что абсолютно не-HTML5-но:
<html xmlns:th="http://www.thymeleaf.org">
… который вообще не влияет на обработку шаблонов, но работает как «заклинание», которое мешает нашей среде IDE жаловаться на отсутствие определения пространства имен для всех этих атрибутов th: *.
Как сделать этот шаблон HTML5? Легко: переключитесь на синтаксис атрибутов Thymeleaf, используя "data-" для имен атрибутов и дефис (-) вместо двоеточия (:):
<!DOCTYPE html>
<html>
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" data-th-href="@{/css/gtvg.css}" />
</head>
<body>
<p data-th-text="#{home.welcome}">Welcome to our grocery store!</p>
</body>
</html>
Пользовательские аттрибуты с префиксом «data-» допускаются спецификацией HTML5, поэтому, используя этот код выше, наш шаблон будет действительным документом HTML5.
Оба обозначения полностью эквивалентны и взаимозаменяемы, но для простоты и компактности примеров кода этот учебник будет использовать нотацию пространства имен «th: *». Кроме того, нотация «th: *» является более общей и разрешена в каждом типе шаблона Thymeleaf (XML, TEXT ...), тогда как префикс аттрибутов «data-» разрешается только в режиме HTML.
Использование th:text и многоязычность текста
Многоязычный текст представлен вне файлов шаблонов и хранится в отдельных файлах (обычно в файлах .properties) и его можно легко заменить эквивалентными текстами, написанными на других языках (процесс, называемый интернационализацией или просто i18n). Такие «внешние фрагменты текста» обычно называются «сообщениями».
В сообщениях всегда есть ключ, который идентифицирует их, а Thymeleaf позволяет указать, что текст должен соответствовать конкретному сообщению с синтаксисом #{...}:
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
Мы можем видеть две особенности Стандартного диалекта Thymeleaf:
Аттрибут "th:text", который обрабатывает собственное значение и устанавливает результат обработки в теле связанного тега, перезаписывая «Welcome to our grocery store!» текст.
Выражение "#{home.welcome}", определяемое Стандартным синтаксисом Выражение, указывая, что текст внутри аттрибута "th:text" должен быть сообщением с ключом «home.welcome», связанным с локалью.
И где здесь многоязычность ?
Место расположения многоязычного текста в Thymeleaf полностью конфигурируемое и определяется реализацией интерфейса org.thymeleaf.messageresolver.IMessageResolver. Обычно, реализация основана на использовании файлов .properties, но мы можем создать нашу собственную реализацию, если захотим, для извлечения сообщений из Базы данных.
В примере мы не определяем message resolver в течение инициализации, что означает — наше приложение использует Standard Message Resolver, реализованный в org.thymeleaf.messageresolver.StandardMessageResolver.
Standard message resolver ищет распложение файлов сообщений /WEB-INF/templates/home.html в файлах «properties» в той же директории с тем же именем, как наш шаблон — пример:
/WEB-INF/templates/home_en.properties для English текста.
/WEB-INF/templates/home_es.properties для Spanish текста.
/WEB-INF/templates/home_pt_BR.properties для Portuguese (Brazil) текста.
/WEB-INF/templates/home.properties для текста по умолчанию (если локаль не определена).
Давайте посмотрим в наш home_es.properties файл:
home.welcome=?Bienvenido a nuestra tienda de comestibles!
Это все, что нам нужно для того, чтобы заставить Thymeleaf обработать шаблон. Давайте теперь создадим Home контроллер.
Контексты/Contexts
Чтобы обработать наш шаблон, мы создадим класс HomeController, реализующий интерфейс IGTVGController, который мы видели ранее:
public class HomeController implements IGTVGController {
public void process(
final HttpServletRequest request, final HttpServletResponse response,
final ServletContext servletContext, final ITemplateEngine templateEngine)
throws Exception {
WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
templateEngine.process("home", ctx, response.getWriter());
}
}
Первое, что мы видим, это создание контекста. Контекст Thymeleaf — это объект, реализующий интерфейс org.thymeleaf.context.IContext. Контексты должны содержать все данные, необходимые для выполнения механизма шаблона в Map переменных, а также ссылаться на языковой стандарт, который должен использоваться для сообщений.
public interface IContext {
public Locale getLocale();
public boolean containsVariable(final String name);
public Set<String> getVariableNames();
public Object getVariable(final String name);
}
Существует специализированное расширение этого интерфейса org.thymeleaf.context.IWebContext, предназначенное для использования в веб-приложениях на основе ServletAPI (например, SpringMVC).
public interface IWebContext extends IContext {
public HttpServletRequest getRequest();
public HttpServletResponse getResponse();
public HttpSession getSession();
public ServletContext getServletContext();
}
В основной библиотеке Thymeleaf представлена реализация каждого из этих интерфейсов:
org.thymeleaf.context.Context реализует IContext
org.thymeleaf.context.WebContext реализует IWebContext
И, как вы можете видеть в коде контроллера, WebContext — это тот, который мы используем. На самом деле это необходимо, потому что использование ServletContextTemplateResolver требует использования контекста, реализующего IWebContext.
WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
Требуется только три из этих четырех аргументов конструктора, потому что будет использоваться дефолтная локаль для системы, если ни одна не указана (хотя вы никогда не должны допускать, чтобы это происходило в реальных приложениях).
Существуют некоторые специализированные выражения, которые мы сможем использовать для получения параметров запроса и атрибутов запроса, сеанса и приложения из WebContext в наших шаблонах. Например:
${x} вернет переменную x, сохраненную в контексте Thymeleaf, или как атрибут запроса.
${param.x} вернет параметр запроса с именем x (который может быть многозначным).
${session.x} вернет атрибут сессии с именем x.
${application.x} вернет атрибут контекста сервлета с именем x.
Запуск template engine
Когда наш объект контекста готов, мы можем сообщить движку шаблона обработать шаблон (по его названию) с использованием контекста и передать ответ, чтобы ответ мог быть внедрен в шаблон:
templateEngine.process("home", ctx, response.getWriter());
Посмотрим результат с использование локали Spanish:
<!DOCTYPE html>
<html>
<head>
<title>Good Thymes Virtual Grocery</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
</head>
<body>
<p>?Bienvenido a nuestra tienda de comestibles!</p>
</body>
</html>
3.2 Больше текста и переменных
Экранированный текст
Простейшая версия нашей домашней страницы, похоже, сейчас готова, но есть кое-что, о чем мы не думали… что, если бы у нас было такое сообщение?
home.welcome=Welcome to our <b>fantastic</b> grocery store!
Если мы выполним этот шаблон, как и раньше, мы получим:
<p>Welcome to our <b>fantastic</b> grocery store!</p>
Это не совсем то, что мы ожидали, потому что наш тег <b> был экранирован, и поэтому он будет отображаться в браузере.
Это поведение по умолчанию атрибута "th:text". Если мы хотим, чтобы Thymeleaf не менял наши теги HTML и не избегал их, нам придется использовать другой атрибут: th:utext («unescaped text»):
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
Это выводит наше сообщение так, как мы этого хотели:
<p>Welcome to our fantastic grocery store!</p>
Использование и отображение переменных
Теперь добавим еще немного контента на нашу домашнюю страницу. Например, мы можем отображать дату ниже нашего приветственного сообщения:
Welcome to our fantastic grocery store!
Today is: 12 july 2010
Прежде всего, нам придется изменить наш контроллер, чтобы добавить эту дату в качестве переменной контекста:
public void process(
final HttpServletRequest request, final HttpServletResponse response,
final ServletContext servletContext, final ITemplateEngine templateEngine)
throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();
WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));
templateEngine.process("home", ctx, response.getWriter());
}
Мы добавили переменную String, названную "today" в наш контекст, и теперь можем отобразить ее в нашем шаблоне:
<body>
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p
<p>Today is: <span th:text="${today}">13 February 2011</span></p>
</body>
Как вы можете видеть, мы все еще используем атрибут "th:text" для работы (и это правильно, потому что мы хотим заменить тело тега), но синтаксис на этот раз немного отличается, а вместо #{...}, мы используем ${...}. Это выражение с переменной и содержит выражение на языке OGNL (Object-Graph Navigation Language), который будет выполняться на контекстных переменных в map, о которой мы говорили ранее.
Выражение ${today} просто означает «получить переменную, называемую today», но эти выражения могут быть более сложными (например, ${user.name} для «получить переменную, называемую user, и вызвать ее метод getName()»).
В значениях атрибутов имеется довольно много возможностей: сообщения, переменные… и многое другое. Следующая глава покажет нам, каковы все эти возможности.
Продолжение. Глава 4. Standard Expression Syntax