- PVSM.RU - https://www.pvsm.ru -
Оглавление [1]
Мы сделаем небольшой перерыв в развитии нашего виртуального магазина бакалейных товаров, чтобы узнать об одной из наиболее важных частей Стандартного диалекта Thymeleaf: Стандарте синтаксиса выражений Thymeleaf.
Мы уже видели два типа допустимых значений атрибутов, выраженные в этом синтаксисе: сообщения и переменные:
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
<p>Today is: <span th:text="${today}">13 february 2011</span></p>
Но существует значительно больше выражений. Первое — сделаем краткий обзор Standard Expression:
Выражения могут комбинироваться и вкладываться:
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))
Как мы знаем, выражение сообщения #{...} позволяет нам связывать:
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
… с:
home.welcome=¡Bienvenido a nuestra tienda de comestibles!
Но есть один аспект, о котором мы еще не подумали: что произойдет, если текст сообщения не будет полностью статическим? Что, если, например, наше приложение знает, какой пользователь посещает сайт, и мы хотели бы приветствовать пользователя по имени?
<p>¡Bienvenido a nuestra tienda de comestibles, John Apricot!</p>
Это означает, что нам необходимо добавить параметр в наше сообщение. Вот так:
home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!
Параметр определяется относительно стандартного синтаксиса java.text.MessageFormat, который означается, что вы можете форматировать числа и даты.
Чтобы указать значение для нашего параметра из атрибут HTTP сеанса, называемого пользователем, мы напишем:
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
Несколько параметров можно определить, разделив запятыми. Так же сами ключи сообщения могут прийти из переменной:
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
Мы уже упоминали, что выражения ${...} на самом деле являются выражениями OGNL (Object-Graph Navigation Language), выполненными на map переменных, содержащихся в контексте.
Для получения подробной информации о синтаксисе и функциях OGNL вы должны прочитать «Руководство по языку OGNL».
В Spring приложениях с поддержкой MVC OGNL будет заменен на SpringEL, но его синтаксис очень похож на синтаксис OGNL (фактически, точно такой же для большинства распространенных случаев).
Из синтаксиса OGNL мы знаем, что выражение:
<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>
эквивалентно:
ctx.getVariable("today");
Но OGNL позволяет создавать более мощные выражения, как это:
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
… получаем имя пользователя:
((User) ctx.getVariable("session").get("user")).getName();
Но getter — это лишь одна из особенностей OGNL. Посмотрим еще:
/*
* Доступ к свойствам с использованием точки (.). Эквивалент вызывающим getter свойств.
*/
${person.father.name}
/*
* Доступ к свойству может быть так же через скобки ([]) и написание
* имени свойства как переменной или между простыми кавычками.
*/
${person['father']['name']}
/*
* Если объект является map, оба синтаксиса точки и скобки будет эквивалентен
* выполнению метода get(...).
*/
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}
/*
* Доступ к массиву или коллекции так же выполняется через скобки,
* с написанием индекса без скобок.
*/
${personsArray[0].name}
/*
* Могут быть вызваны методы даже с аргументами.
*/
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}
Expression Basic Objects
При выполнении выражения OGNL с переменными контекста некоторые объекты становятся доступными для большей гибкости. На эти объекты можно ссылаться (по стандарту OGNL), начиная с символа #:
#ctx: контекст.
#vars: переменные контекста.
#locale: локаль контекста.
#request: (только в Web Contexts) объект HttpServletRequest.
#response: (только в Web Contexts) объект HttpServletResponse.
#session: (только в Web Contexts) объект HttpSession.
#servletContext: (только в Web Contexts) объект ServletContext.
Так же мы можем делать следующее:
<span th:text="${#locale.country}">US</span>
Вы можете прочитать полные ссылки на эти объекты в Приложении A.
Утилиты выражений
Помимо этих основных объектов, Thymeleaf предлогает нам набор полезных объектов, которые помогут нам выполнять общие задачи в наших выражениях.
#execInfo: информация о текущем шаблоне.
#messages: методы для получения сообщений внутри выражений, так же, как они будут получены с использованием синтаксиса #{...}.
#uris: методы для экранирования частей URL/URI.
#conversions: методы для выполнения настроек службы преобразования (если присутствуют).
#dates: методы для java.util.Date objects: форматирование, извлечение компонентов и тп.
#calendars: аналогично #dates, но для java.util.Calendar объектов.
#numbers: методы для форматирования числовых объектов.
#strings: методы для String объекто: contains, startsWith, prepending/appending и тп.
#objects: общие методы для объектов.
#bools: методы для булевых преобразований.
#arrays: методы для массивов.
#lists: методы для List.
#sets: методы для Sets.
#maps: методы для Maps.
#aggregates: методы для аггрегирования массовов или коллекций.
#ids: методы для обработки идентификаторов id, которые могут быть повторены (например, в результате итерации).
Вы можете проверить, какие функции предлагаются для каждого из этих объектов в Приложении B.
Переформатирование даты на странице Home
Теперь мы знаем об этих утилитах объектов и можем использовать их, чтобы изменить способ отображения даты на нашей домашней странице. Вместо этого в нашем HomeController:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();
WebContext ctx = new WebContext(request, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));
templateEngine.process("home", ctx, response.getWriter());
… мы можем сделать следующее:
WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());
templateEngine.process("home", ctx, response.getWriter());
… и затем выполнить форматирование даты в самом слое представления:
<p>
Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>
Выражения с переменными могут быть записаны не только через ${...}, но и как *{...}.
Между вариантами существует разница: синтаксис со звездочкой преобразует выражение над выбранным объектом нежели над всем контекстом. Это значит, что если нет выбранного объекта, доллар и звездочка делают одно и то же.
Что такое выбранный объект? Результат выражения с использованием атрибута "th:object". Используем его на странице профиля (userprofile.html):
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
Что эквивалентно:
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>
Конечно, доллар и звездочка могут смешиваться:
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
Когда выделенный объект находится на месте, выбранный объект будет так же доступен с долларом, как и #object переменная:
<div th:object="${session.user}">
<p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
Как сказано, если нет выбранного объекта, доллар и звездочка эквиваленты.
<div>
<p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>
Из-за своей важности URL-адреса являются первоклассными «гражданами» в шаблонах веб-приложений, а Стандартный диалект Thymeleaf имеет для них специальный "@" синтаксис: @{...}
Существуют разные типы URL:
Реальная обработка этих выражений и их преобразование в URL-адреса, которые будут выводиться, выполняются с помощью реализации интерфейса org.thymeleaf.linkbuilder.ILinkBuilder, которые регистрируются в используемом объекте ITemplateEngine.
По умолчанию одна реализация этого интерфейса представлена классом org.thymeleaf.linkbuilder.StandardLinkBuilder, которого достаточно как для автономных (не веб-сайтов), так и для веб-сценариев на основе API-интерфейса Servlet. Другие сценарии (например, интеграция с веб-фреймворками, не относящимися к ServletAPI), возможно, потребуют конкретных реализаций интерфейса компоновщика ссылок (link builder interface).
Давайте используем новый синтаксис. Встречайте атрибут "th:href":
<!-- Создаем 'http://localhost:8080/gtvg/order/details?orderId=3' (плюс реврайт) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Создаем '/gtvg/order/details?orderId=3' (плюс реврайт) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Создаем '/gtvg/order/3/details' (плюс реврайт) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
Несколько заметок:
Как и в случае синтаксиса сообщения (#{...}), URL также могут быть результатом выполнения другого выражения:
<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
Меню для нашей домашней страницы
Сейчас мы знаем, как создать ссылки URLs, что насчет добавления небольшого навигационного меню на нашу Home страницу?
<p>Please select an option</p>
<ol>
<li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
<li><a href="order/list.html" th:href="@{/order/list}">Order List</a></li>
<li><a href="subscribe.html" th:href="@{/subscribe}">Subscribe to our Newsletter</a></li>
<li><a href="userprofile.html" th:href="@{/userprofile}">See User Profile</a></li>
</ol>
Ссылки URLs относительно корневой директории сервера
Дополнительный синтаксис может использоваться для создания URL-адресов, основанных на корневом каталоге (вместо контекстно-зависимых) URL-адресов, чтобы ссылаться на разные контексты на одном сервере. Эти URL-адреса будут указаны как @{~/path/to/something}
Фрагментные выражения — это простой способ представить фрагменты разметки и перемещать их между шаблонами. Это позволяет нам копировать, передавать их другим шаблонам в качестве аргументов и тп.
Наиболее распространенное использование — для вставки фрагмента с использованием th:insert или th:replace (подробнее об этом в следующем разделе):
<div th:insert="~{commons :: main}">...
Но они могут использоваться везде, как и любая другая переменная:
<div th:with="frag=~{footer :: #main/text()}">
<p th:insert="${frag}">
</div>
Позже в этом учебном курсе есть целый раздел, посвященный Template Layout, включая более глубокое объяснение фрагментных выражений.
Текстовые литералы
Текстовые литералы — это только символьные строки, заданные между одинарными кавычками. Они могут включать любой символ, но вы должны избегать каких-либо одиночных кавычек внутри них, используя '.
<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>
Числовые литералы
Числовые литералы — это просто числа.
<p>The year is <span th:text="2013">1492</span>.</p>
<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>
Булевые/Boolean литералы
Булевые литералы — это true и false. Для примера:
<div th:if="${user.isAdmin()} == false"> ...
В этом примере "== false" написаны вне скобок, то о выражении заботится Thymeleaf. Если бы равенство содержалось внутри скобок — о нем бы заботился OGNL/SpringEL движок:
<div th:if="${user.isAdmin() == false}"> ...
Литерал null
Литерал null так же может быть использован:
<div th:if="${variable.something} == null"> ...
Литеральные токены
Числовые, булевые и null литералы являются частным случаем литеральных токенов.
Эти токены позволяют немного упростить Standard Expressions. Они работают точно так же, как текстовые литералы ('...'), но допускают только буквы (A-Z и a-z), цифры (0-9), скобки ([ и ]), точки (.), дефисы (-) и подчеркивания (_). Никаких пробелов, никаких запятых и тп.
Хорошая часть? Токены не нуждаются в кавычках, окружающих их. Поэтому мы можем сделать:
<div th:class="content">...</div>
вместо:
<div th:class="'content'">...</div>
Тексты, независимо от того, являются ли они литералами или результатом обработки переменных или сообщений, могут быть легко добавлены с помощью оператора +:
<span th:text="'The name of the user is ' + ${user.name}">
Литеральные замены позволяют легко форматировать строки, содержащие значения из переменных, без необходимости добавлять литералы с помощью '...' + '...'.
Эти замены должны быть окружены вертикальными черточками (|), например:
<span th:text="|Welcome to our application, ${user.name}!|">
Что эквивалентно:
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
Литеральные замены могут быть объединены с другими типами выражений:
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
Только выражения переменной / сообщения (${...}, *{...}, #{...}) разрешены внутри |...| литеральных замен. Никакие другие литералы ('...'), boolean/numeric токены, условные выражения и тп.
Некоторые арифметические операции так же доступны: +, -, *, /, %.
<div th:with="isEven=(${prodStat.count} % 2 == 0)">
Обратите внимание, что эти операторы также могут быть применены непосредственно в самих выражениях OGNL (и в этом случае будет выполняться OGNL вместо механизма Thymeleaf Standard Expression):
<div th:with="isEven=${prodStat.count % 2 == 0}">
Обратите внимание, что для некоторых из этих операторов существуют текстовые псевдонимы: div (/), mod (%).
Значения в выражениях можно сравнить с символами >, <, >= и <=, ==, != операторы используются для проверки равенства (или его отсутствия). Обратите внимание, что XML устанавливает < и > символы не должны использоваться в значениях атрибутов, и поэтому они должны быть заменены на < и>.
<div th:if="${prodStat.count} > 1">
<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
Более простая альтернатива может заключаться в использовании текстовых псевдонимов, существующих для некоторых из этих операторов: gt (>), lt (<), ge (>=), le (<=), not (!). Так же eq (==), neq/ne (!=).
Условные выражения предназначены для выполнения только одного из двух выражений в зависимости от результата оценки условия (которое само является другим выражением).
Давайте посмотрим на фрагмент примера (введение другого модификатора атрибута, th:class):
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
Все три части условного выражения (condition, then и else) сами являются выражениями, что означает, что они могут быть переменными (${...}, *{...}), messages (#{...}), URLs (@{...}) или литералами ('...').
Условные выражения также могут быть вложены с помощью круглых скобок:
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
...
</tr>
Другие выражения также могут быть опущены, и в этом случае возвращается null значение, если условие ложно:
<tr th:class="${row.even}? 'alt'">
...
</tr>
Выражение по умолчанию — это особый вид условного значения без какой-либо части. Это эквивалентно оператору Elvis, присутствующему на некоторых языках, например Groovy, что позволяет указать два выражения: первый используется, если он не null, но если это так, то используется второй.
Посмотрим, как это работает на странице нашего профиля:
<div th:object="${session.user}">
...
<p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>
Как вы можете видеть, оператором является ?:, И мы используем его здесь, чтобы указать значение по умолчанию для имени (буквальное значение в этом случае), только если результат вычисления *{age} равен null. Поэтому это эквивалентно:
<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>
Как и условные значения, они могут содержать вложенные выражения между круглыми скобками:
<p>
Name:
<span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastian</span>
</p>
Токен No-Operation представлен символом (_).
Идея этого токена состоит в том, чтобы указать, что желаемый результат для выражения — ничего не делать: делать точно так, как если бы обработанный атрибут (например, th:text) вообще отсутствовал.
Среди других возможностей это позволяет разработчикам использовать текст прототипов в качестве значений по умолчанию. Например, вместо:
<span th:text="${user.name} ?: 'no user authenticated'">...</span>
… мы можем напрямую использовать ‘no user authenticated’ в качестве текста прототипирования, что приводит к тому, что код является более кратким и универсальным с точки зрения дизайна:
<span th:text="${user.name} ?: _">no user authenticated</span>
Thymeleaf определяет синтаксис двойных скобок для переменных (${...}) и выделенных (*{...}) выражений, что позволяет нам применя преобразование данных с использование конфигурирования сервиса преобразований.
Базово это выглядит так:
<td th:text="${{user.lastAccessDate}}">...</td>
Вы заметили двойную скобку?: ${{...}}. Это инструктирует Thymeleaf передать результат выражения user.lastAccessDate в службу преобразования и попросит выполнить операцию форматирования (преобразование в String) перед возвращением результата.
Предполагая, что user.lastAccessDate имеет тип java.util.Calendar, если служба преобразования (реализация IStandardConversionService) была зарегистрирована и содержит действительное преобразование для Calendar -> String, оно будет применяться.
По умолчанию реализация IStandardConversionService (класс StandardConversionService) просто выполняет .toString() для любого объекта. Дополнительные сведения о том, как зарегистрировать пользовательскую реализацию службы преобразования, можно найти в разделе «Дополнительные сведения о конфигурации».
Официальные пакеты интеграции thymeleaf-spring3 и thymeleaf-spring4 прозрачно интегрируют механизм обслуживания преобразования Thymeleaf с собственной инфраструктурой Conversion Service Spring, так что сервисы конвертации и форматы, объявленные в конфигурации Spring, будут автоматически доступны для выражений ${{...}} и *{{...}}.
В дополнение ко всем этим функциям для обработки выражений Thymeleaf имеет функцию препроцессорных выражений.
Предварительная обработка — это выполнение выражений, сделанных до выполнения стандартных выражений, что позволяет модифицировать выражение, которое в конечном итоге будет выполнено.
Предварительно обработанные выражения точно аналогичны нормальным, но появляются в окружении двойного символа подчеркивания (например, __${выражение}__).
Предположим, у нас есть запись i18n Messages_fr.properties, содержащая выражение OGNL, вызывающее статический метод, специфичный для языка, например:
article.text=@myapp.translator.Translator@translateToFrench({0})
… и эквивалент Messages_es.properties:
article.text=@myapp.translator.Translator@translateToSpanish({0})
Мы можем создать фрагмент разметки, который выполняет одно выражение или другое в зависимости от локали. Для этого мы сначала выберем выражение (путем предварительной обработки), а затем запустим Thymeleaf:
<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>
Обратите внимание, что шаг предварительной обработки для французского языка будет создавать следующий эквивалент:
<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>
Строка препроцессора __ может быть экранирована в атрибутах с помощью __.
Автор: pilot911
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/275122
Ссылки в тексте:
[1] Оглавление: https://habrahabr.ru/post/350862/
[2] www.thymeleaf.org: http://www.thymeleaf.org
[3] Источник: https://habrahabr.ru/post/350870/?utm_campaign=350870
Нажмите здесь для печати.