Дерево страниц

Сравнение версий

Ключ

  • Эта строка добавлена.
  • Эта строка удалена.
  • Изменено форматирование.

2. Краткий обзор

Чтобы внедрить динамический контент на наши страницы, нам нужно использовать синтаксис, который понимает FreeMarker :

${…} в шаблоне будет заменено в сгенерированном выводе фактическим

Основные правила

Метка в шаблоне

Метка в шаблоне обозначается ${…}. Метка заменяется значением выражения внутри фигурных скобок

— мы называем это интерполяцией — несколько примеров: 

. Примеры: ${1 + 2

 

}

и 

, ${variableName} 

Теги

Теги FTL похожи на теги HTML (но

содержат # или @ 

содержат # или @ ), и FreeMarker интерпретирует их, например, <#if…></#if>

Комментарии

Комментарии в FreeMarker начинаются

с <#– и заканчиваются 

с <#– и заканчиваются -->

Мы можем использовать ?? оператор, чтобы проверить,
Свойства страницы
Статус

Статус
colour

Yellow

Green
title

В работеКомментарий

3. Тег включения

Директива включения FTL позволяет нам следовать принципу DRY в нашем приложении. Мы определим повторяющийся контент в файле и повторно используем его в разных шаблонах FreeMarker с одним включенным тегом.

Один из таких вариантов использования — когда мы хотим включить раздел меню на многие страницы. Во-первых, мы определим раздел меню внутри файла — назовем его menu.ftl — со следующим содержимым:

<a href="#dashboard">Dashboard</a>
<a href="#newEndpoint">Add new endpoint</a>

И на нашу HTML-страницу давайте включим созданный файл menu.ftl :

<!DOCTYPE html>
<html>
<body>
<#include 'fragments/menu.ftl'>
<h6>Dashboard page</h6>
</body>
</html>

И мы также можем включить FTL в наши фрагменты, и это здорово.

4. Обработка существования ценности

FTL будет рассматривать любое нулевое значение как отсутствующее значение. Таким образом, нам нужно быть особенно осторожными и добавить логику для обработки null внутри нашего шаблона.

ГОТОВО

РазделFreeMarker

Операторы

ОператорОписание
??Проверить существует ли атрибут или вложенное свойство
. Результатом является логическое значение:
${attribute
?
?}

Итак, мы проверили атрибут на null, но этого не всегда достаточно. Давайте теперь определим значение по умолчанию в качестве запасного варианта для этого отсутствующего значения. Для этого нам понадобится ! оператор ставится после имени переменной:

${attribute!'default value'}

Используя круглые скобки, мы можем обернуть многие вложенные атрибуты.

Например, чтобы проверить,
cПреобразовать логическое значение в удобочитаемую строку
!Значение по умолчанию, если атрибут не существует
()Проверить существует ли атрибут и имеет ли он вложенное свойство с другим вложенным свойством
, мы обертываем все:
${(attribute.nestedProperty.nestedProperty)??}

Наконец, собрав все вместе, мы можем встроить их среди статического контента:

<p>Testing

Пример

Блок кода
languagexml
titleШаблон
<p>Testing is student property exists: ${student???c}</
p>
<p>Using
p>
<p>Using default value for missing student: ${student!'John Doe'}</
p>
<p>Wrapping
p>
<p>Wrapping student nested properties: ${(student.address.street)???c}</
p>

И, если бы student был null , мы бы увидели:

<p>Testing
p>
Блок кода
languagexml
titleРезультат
<p>Testing is student property exists: 
false</p>
<p>Using
false</p>
<p>Using default value for missing student: John 
Doe</p>
<p>Wrapping
Doe</p>
<p>Wrapping student nested properties: 
false<
false</
p>

Обратите внимание на дополнительную директиву ?c , используемую после ?? . Мы сделали это, чтобы преобразовать логическое значение в удобочитаемую строку.

5. Тег «если-иначе»

p>

Тег «Условие»

Управляющие структуры присутствуют во FreeMarker, и традиционный if-else

, вероятно, вам знаком:<#if condition>

Блок кода
languagexml
<#if condition>
    <!-- block to execute if condition is true -->

<#elseif condition2>

<#elseif condition2>
    <!-- block to execute if condition2 is the first true condition -->

<#elseif condition3>

<#elseif condition3>
    <!-- block to execute if condition3 is the first true condition -->

<#else>

<#else>
    <!-- block to execute if no condition is true -->


</
#if>

Хотя ветки elseif и else необязательны, условия должны разрешаться в логическое значение.

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

  • x == y , чтобы проверить, равен ли x y
  • x != y , чтобы вернуть true , только если x отличается от y
  • x lt y означает, что x должен быть строго меньше y — мы также можем использовать < вместо lt
  • x gt y оценивается как true , только если x строго больше y — мы можем использовать > вместо gt
  • x lte y проверяет , меньше ли x или равно y – альтернативой lte является <=
  • x gte y проверяет, больше ли x или равно y – альтернатива gte >=
  • Икс?? проверить существование x
  • sequence?seqContains(x) проверяет наличие x внутри последовательности

Очень важно помнить, что FreeMarker рассматривает >= и > как закрывающие символы для тега FTL. Решение состоит в том, чтобы заключить их использование в круглые скобки или вместо этого использовать gte или gt .

Собираем все вместе по следующему шаблону:

<#if
#if>

Пример

Блок кода
languagexml
titleШаблон
<#if status??>


    
<p>$
<p>${status.reason}</
p>
<#else>
p>
<#else>
    
<p>Missing
<p>Missing status!</
p>
</#if>
В итоге получаем HTML-код:
p>
</#if>
Блок кода
languagexml
titleШаблон
<!-- When status attribute exists -->

<p>404

<p>404 Not 
Found</p>
Found</p>

<!-- When status attribute is missing -->

<p>Missing

<p>Missing status!</
p>
p>
6.

Контейнеры подпеременных

Во FreeMarker у нас есть три типа контейнеров для подпеременных:

  • Хэши  - это последовательность пар ключ-значение — ключ должен быть уникальным внутри хеша, и у нас нет порядкаПоследовательности 
  • Последовательности  - это списки, в которых у нас есть индекс, связанный с каждым значением — примечательным фактом является то, что подпеременные могут быть разных типов.
  • Коллекции  - это особый случай последовательностей, где мы не можем получить доступ к размеру или получить значения по индексу — мы все же можем перебирать их с помощью тега списка ! <#list>
6.1.

Повторение элементов

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

<#list

 

Блок кода
languagexml
titleСпособ 1
<#--Перебираем каждое значение, и для каждого из них выполняется логика-->
<#list sequence as 
item>
item>
    <!-- do something with ${item} -->


</
#list>
#list>

<#--Или, когда мы хотим повторить
 
 Hash
 
 , получая доступ как к ключу, так и к
значению:<#list
 значению -->
<#list hash as key, 
value>
value>
    <!-- do something with ${key} and ${value} -->


</
#list>

Вторая форма более мощная, потому что она также позволяет нам определить логику, которая должна происходить на различных этапах итерации:

<#list sequence>
#list>
Блок кода
languagexml
titleСпособ 2
<#-- Позволяет определить логику, которая должна происходить на различных этапах итерации. Элемент представляет собой имя зацикленной переменной, 
но можно переименовать его по своему усмотрению --> 
<#list sequence>
    <!-- one-time logic if the sequence is not empty -->


    
<#items
<#items as 
item>
item>
        <!-- logic repeated for every item in sequence -->


    </
#items>
#items>
    <!-- one-time logic if the sequence is not empty -->

<#else>

<#else>
    <!-- one-time logic if the sequence is empty -->


</
#list>

Элемент представляет собой имя зацикленной переменной, но мы можем переименовать его по своему усмотрению . Ветка else не является обязательной.

Для практического примера хорошо определите шаблон, в котором мы перечисляем некоторые статусы:

<#list statuses>
<ul>
<#items as status>
#list>


Пример

Блок кода
languagexml
titleШаблон
<#list statuses>
    <ul>
    <#items as status>
        
<li>$
<li>${status}</
li>
li>
    </
#items>
#items>
    </
ul>
<#else>
ul>
<#else>
    
<p>No
<p>No statuses 
available</p>
available</p>
</
#list>
Это вернет нам следующий HTML-код, когда наш
#list>
 контейнер ["200 OK", "404 Not Found", "500 Internal Server Error"] :<ul>
<li>200 OK</li>
<li>404 Not Found</li>
<li>500
Блок кода
languagexml
titleРезультат
<ul>
<li>200 OK</li>
<li>404 Not Found</li>
<li>500 Internal Server 
Error</li>
Error</li>
</
ul>
6.2.
ul>

Обработка предметов

Хэш позволяет нам выполнять две простые функции: keys keys для извлечения только содержащихся ключей и values values для извлечения только значений.

Последовательность более сложная; мы можем можно сгруппировать наиболее полезные функции:

  • фрагмент chunk и соединение join, чтобы получить подпоследовательность или объединить две последовательности
  • reverse reverse , sort sort и sortBy sortBy для изменения порядка элементов
  • first first и last last извлекут первый или последний элемент соответственно
  • size size представляет количество элементов в последовательности
  • seqContains seqContains , seqIndexOf seqIndexOf или seqLastIndexOf seqLastIndexOf для поиска элемента
7.

Обработка типов

FreeMarker поставляется с огромным количеством разнообразных функций (встроенных), доступных для работы с объектами. Давайте посмотрим на некоторые часто используемые функции.

7.1.

Обработка строк

url и urlPath 
  • url и urlPath будут экранировать строку URL-адресом, за исключением того,
что 
  • что urlPath
 не
  •  не будет экранировать косую черту /
jString 
  • jString , 
jsString и jsonString 
  • jsString и jsonString будут применять правила экранирования для Java, Javascript и JSON соответственно .
capFirst 
  • capFirst , 
uncapFirst 
  • uncapFirst , 
upperCase 
  • upperCase , 
lowerCase и capitalize 
  • lowerCase и capitalize полезны для изменения регистра
нашей
  • строки, как следует из их названий.
boolean 
  • boolean , 
date 
  • date , 
time 
  • time , 
datetime и number 
  • datetime и number - это функции для преобразования строки в другие типы.

Давайте теперь воспользуемся некоторыми из этих функций:

<p>$

Пример

Блок кода
languagexml
titleШаблон
<p>${'http://myurl.com/?search=Hello World'?urlPath}</
p>
<p>$
p>
<p>${'Using " in text'?jsString}</
p>
<p>$
p>
<p>${'my value?upperCase}</
p>
<p>$
p>
<p>${'2019-01-12'?date('yyyy-MM-dd')}</
p>

И вывод для шаблона выше будет:

<p>http%3A
p>
Блок кода
languagexml
titleРезультат
<p>http%3A//myurl.com/
%3Fsearch%3DHello%20World</p>
<p>MY VALUE</p>
<p>Using
%3Fsearch%3DHello%20World</p>
<p>MY VALUE</p>
<p>Using \" in 
text</p>
<p>12
text</p>
<p>12.01.
2019<
2019</
p>

При использовании функции даты мы также передаем шаблон для анализа объекта String. FreeMarker использует локальный формат, если не указано иное , например, в строковой функции, доступной для объектов даты.

7.2. Обработка номеров

round , пол и потолок 
p>

Обработка чисел

  • round min и max могут помочь с округлением чисел
abs 
  • abs вернет абсолютное значение числа
string 
  • string преобразует число в строку. Мы также можем передать четыре предопределенных числовых формата:
 компьютер , валюта , число или процент или
  •  компьютер , валюта , число или процент или определить свой собственный формат,
например 
  • например ["0.###"]

Сделаем цепочку из нескольких математических операций:

<p>$

Пример

Блок кода
languagexml
titleШаблон
<p>${(7.3?round + 3.4?ceiling + 0.1234)?string('0.##')}</
p>
<!
p>
<#-- (7 + 4 + 0.1234) with 2 decimals -->

И, как и ожидалось, результирующее значение равно 11,12.

7.3.
Блок кода
languagexml
titleРезультат
<p>11.12</p>

Обработка даты

  • .now
 представляет
  •  представляет текущую дату и время
date 
  • date , 
time и datetime 
  • time и datetime могут возвращать разделы даты и времени объекта даты и времени.
строка преобразует
  • строка преобразует дату и время в строки
— мы также можем
  • - можно передать желаемый формат или использовать предопределенный

Теперь мы собираемся получить текущее время и отформатировать вывод в строку, содержащую только часы и минуты:

<p>$


Блок кода
languagexml
titleШаблон
<p>${.now?time?string('HH:mm')}</
p>

Результирующий HTML будет:

<p>15:39</p>

8. Обработка исключений

Мы увидим два
p>
Блок кода
languagexml
titleРезультат
<p>15:39</p>

Обработка исключений

Два способа обработки исключений для шаблона FreeMarker.Первый способ — использовать

  1. Использовать теги попытки

восстановления 
  1. recover, чтобы определить, что мы должны попытаться выполнить, и блок кода, который должен выполниться в случае ошибки.

Синтаксис:

<#attempt>
  1. Блок кода
    languagexml
    titleШаблон
    <#attempt>
        <!-- block to try -->

<#recover>
  1. 
    <#recover>
        <!-- block to execute in case of exception -->

  1. 
    </
#attempt>
  1. #attempt>

    Теги

 попытки и восстановления 
  1. attempt и recover являются обязательными. В случае ошибки он откатывает предпринятый блок и выполняет только код в разделе

 восстановления .

Помня об этом синтаксисе, давайте определим наш шаблон как:

<p>Preparing to evaluate</p>
<#attempt>
<p>Attribute is ${attributeWithPossibleValue??}</p>
<#recover>
<p>Attribute is missing</p>
</#attempt>
<p>Done with the evaluation</p>

Когда attributeWithPossibleValue отсутствует, мы увидим:

<p>Preparing to evaluate</p>
<p>Attribute is missing</p>
<p>Done with the evaluation</p>

И вывод, когда attributeWithPossibleValue существует:

<p>Preparing to evaluate</p>
<p>Attribute is 200 OK</p>
<p>Done with the evaluation</p>
Второй способ — настроить
  1. recover.

  2. Настроить FreeMarker, что должно происходить в случае исключений.

С Spring Boot мы легко настраиваем это через файл свойств; вот некоторые доступные конфигурации:

  • spring.freemarker.setting.template_exception_handler=rethrow повторно выдает исключение
  • spring.freemarker.setting.template_exception_handler=debug выводит клиенту информацию о трассировке стека, а затем повторно выдает исключение.
  • spring.freemarker.setting.template_exception_handler=html_debug выводит клиенту информацию о трассировке стека, форматируя ее так, чтобы она обычно хорошо читалась в браузере, а затем повторно выдает исключение.
  • spring.freemarker.setting.template_exception_handler=ignore пропускает ошибочные инструкции, позволяя шаблону продолжить выполнение.
  • spring.freemarker.setting.template_exception_handler = по умолчанию

9. Методы вызова

Иногда мы хотим вызывать методы Java из наших шаблонов FreeMarker. Сейчас мы посмотрим, как это сделать.

9.1. Статические члены

Чтобы начать доступ к статическим членам, мы можем либо обновить нашу глобальную конфигурацию FreeMarker, либо добавить в модель атрибут типа S taticModels под именем атрибута statics :

model.addAttribute("statics", new DefaultObjectWrapperBuilder(new Version("2.3.28"))
.build().getStaticModels());

Доступ к статическим элементам прост.

Сначала мы импортируем статические элементы нашего класса, используя тег assign, затем выбираем имя и, наконец, путь к классам Java.

Вот как мы импортируем класс Math в наш шаблон, показываем значение статического поля PI и используем статический метод pow :

<#assign MathUtils=statics['java.lang.Math']>
<p>PI value: ${MathUtils.PI}</p>
<p>2*10 is: ${MathUtils.pow(2, 10)}</p>

Результирующий HTML:

<p>PI value: 3.142</p>
<p>2*10 is: 1,024</p>

9.2. Члены бина

Доступ к членам компонента очень прост: используйте точку (.) , и все!

В нашем следующем примере мы добавим в нашу модель объект Random :

model.addAttribute("random", new Random());

В нашем шаблоне FreeMarker сгенерируем случайное число:

<p>Random value: ${random.nextInt()}</p>

Это приведет к выводу, подобному:

<p>Random value: 1,329,970,768</p>

9.3. Пользовательские методы

Первым шагом для добавления пользовательского метода является наличие класса, который реализует интерфейс TemplateMethodModelEx FreeMarker и определяет нашу логику внутри метода exec :

public class LastCharMethod implements TemplateMethodModelEx {
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 1 || StringUtils.isEmpty(arguments.get(0)))
throw new TemplateModelException("Wrong arguments!");
String argument = arguments.get(0).toString();
return argument.charAt(argument.length() - 1);
}
}

Мы добавим экземпляр нашего нового класса в качестве атрибута модели:

model.addAttribute("lastChar", new LastCharMethod());

Следующим шагом будет использование нашего нового метода внутри нашего шаблона:

<p>Last char example: ${lastChar('mystring')}</p>

Наконец, в результате получается:

<p>Last char example: g</p>



Оглавление