Apache FreeMarker — це шаблонізатор для генерації будь-якого виду текстового виведення від HTML до електронної пошти тощо. Spring Framework має вбудовану інтеграцію для використання Spring MVC з шаблонами FreeMarker.

Конфігурація подання

У наступному прикладі показано, як налаштувати FreeMarker як технологію подання:

Java

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.freeMarker();
    }
    // Конфігуруємо FreeMarker...
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
        return configurer;
    }
}
Kotlin

@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
    override fun configureViewResolvers(registry: ViewResolverRegistry) {
        registry.freeMarker()
    }
    // Конфігуруємо FreeMarker...
    @Bean
    fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
        setTemplateLoaderPath("/WEB-INF/freemarker")
    }
}

У наступному прикладі показано, як налаштувати те саме на XML:


<mvc:annotation-driven/>
<mvc:view-resolvers>
    <mvc:freemarker/>
</mvc:view-resolvers>
            <!-- Configuring FreeMarker... -->
<mvc:freemarker-configurer>
    <mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>

До того ж, можна оголосити бін FreeMarkerConfigurer для повного контролю над усіма властивостями, як показано в наступному прикладі:


<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>

Твої шаблони повинні зберігатися в директорії, вказаній FreeMarkerConfigurer, як показано в попередньому прикладі. Враховуючи попередню конфігурацію, якщо твій контролер повертає ім'я подання welcome, перетворювач шукає шаблон /WEB-INF/freemarker/welcome.ftl.

Конфігурація FreeMarker

Можна передати "Settings" і "SharedVariable" обробника FreeMarker безпосередньо до об'єкта Конфігурація обробника FreeMarker (який керується Spring), встановивши відповідні властивості біна в FreeMarkerConfigurer. Для якості freemarkerSettings потрібен об'єкт java.util.Properties, а для якості freemarkerVariablesjava.util.Map. У цьому прикладі показано, як використовувати FreeMarkerConfigurer:


<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
    <property name="freemarkerVariables">
        <map>
            <entry key="xml_escape" value-ref="fmXmlEscape"/>
        </map>
    </property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>

Подробиці про налаштування та змінні, які застосовуються до об'єкта Конфігурація, див. у документації FreeMarker.

Обробка форм

Spring передбачає бібліотеку тегів для використання в JSP, яка містить, серед іншого, елемент <spring:bind/>. Цей елемент насамперед дозволяє формам виводити на екран значення з базових об'єктів форми та відображати результати невдалих перевірок з Validator на вебрівні або бізнес-рівні. Spring також має підтримку тієї ж функціональності FreeMarker, з додатковими зручними макросами для генерації самих елементів введення форми.

Макроси прив'язки

Засоби підтримки стандартного набору макросів знаходяться у файлі spring- webmvc.jar для FreeMarker, тому вони завжди доступні для відповідного настроєного додатка.

Деякі макроси, визначені в бібліотеках шаблонізації в Spring, вважаються внутрішніми (приватними), але в визначеннях макросів такого обмеження немає, що робить все макроси видимими для коду, який робить виклик, і користувацьких шаблонів. Наступні розділи присвячені лише макросам, які необхідно викликати безпосередньо із шаблонів. Якщо тобі потрібно переглянути код макросу безпосередньо, файл називається spring.ftl і знаходиться в пакеті org.springframework.web.servlet.view.freemarker.

Просте прив'язування

У твоїх HTML-формах на базі шаблонів FreeMarker, які діють як подання форми для контролера Spring MVC, можна використовувати код, подібний до того, що представлений в наступному прикладі, для прив'язки до значень полів і виведення на екран повідомлень про помилки кожного поля введення аналогічно еквіваленту JSP. У цьому прикладі показано подання personForm:


<!-- Макроси FreeMarker повинні бути імпортовані в простір імен.
    Ми рекомендуємо дотримуватись параметра "spring". -->
<#import "/spring.ftl" as spring/>
<html>
    ...
    <form action="" method="POST">
        Name:
        <@spring.bind "personForm.name"/>
        <input type="text"
               name="${spring.status.expression}"
               value="${spring.status.value?html}"/><br/>
        <#list spring.status.errorMessages as error> <b>${error}</b> <br/> </#list>
        <br />
        ...
        <input type="submit" value="submit"/>
    </form>
    ...
</html>

<@spring.bind> вимагає аргумент "path", який складається з імені твого об'єкта команди (це "command", якщо не змінити його в конфігурації контролера), за яким слідує точка та ім'я поля об'єкта команди, до якого ти хочеш здійснити прив'язку. Також можна використовувати вкладені поля, наприклад, command.address.street. Макрос bind передбачає логіку HTML-екранування за замовчуванням, встановлену параметром defaultHtmlEscape контексту ServletContext у web.xml.

Альтернативна форма макросу <@spring.bindEscaped> приймає другий аргумент, який явно вказує, чи слід використовувати HTML-екранування в повідомленнях про помилку визначення статусу чи значення. Залежно від необхідності можна встановити значення true або false. Додаткові макроси для роботи з формами полегшують використання HTML-екранування, і тобі варто використовувати ці макроси скрізь, де це можливо. Вони описані в наступному розділі.

Макроси введення

Додаткові допоміжні макроси для FreeMarker спрощують прив'язку та генерацію форм (включно з виведенням на екран помилок валідації). Немає необхідності використовувати ці макроси для створення полів введення форми: їх можна змішувати та поєднувати за допомогою простого HTML або прямих викликів макросів прив'язки Spring, про які ми розповідали раніше.

У цій таблиці доступних макросів наведено визначення шаблону FreeMarker Template (FTL) та список параметрів, які приймає кожен із них:

Таблиця 6. Таблиця макровизначень
макрос Визначення FTL

message (виводить рядок з ресурсного локалізаційного пакета на основі параметра коду)

<@spring.message code/>

messageText (виводить рядок з ресурсного локалізаційного пакета на основі параметра коду, повертаючись до значення параметра за замовчуванням)

<@spring.messageText code, text/>

url (префікс відносної URL-адреси з контекстним коренем програми)

<@spring.url relativeUrl/>

formInput (стандартне поле введення для збору введеної інформації користувача)

<@spring.formInput path, attributes, fieldType/>

formHiddenInput (приховане поле введення для передачі інформації, яку вводить не користувач)

<@spring.formHiddenInput path, attributes/>

formPasswordInput (стандартне поле введення для збирання паролів). Зверни увагу, що в полях цього типу ніколи не заповнюється жодне значення).

<@spring.formPasswordInput path, attributes/>

formTextarea (велике текстове поле для збирання довгого тексту довільної форми)

<@spring. formTextarea path, attributes/>

formSingleSelect (випадне вікно опцій, що дозволяє обрати одне необхідне значення)

<@spring.formSingleSelect path, options, attributes/>

formMultiSelect (поле зі списком опцій, що дозволяє користувачеві обрати 0 або більше значень)

<@spring.formMultiSelect path, options, attributes/>

formRadioButtons (набір перемикачів (радіокнопок), що дозволяють зробити один вибір із наявних варіантів)

<@spring.formRadioButtons path, options separator, attributes/>

formCheckboxes (набір прапорців, що дозволяють обрати 0 або більше значень)

<@spring.formCheckboxes path, options, separator, attributes/>

formCheckbox (один прапорець)

<@spring.formCheckbox path, attributes/>

showErrors (спрощує виведення на екран помилок валідації для пов'язаного поля)

<@spring.showErrors separator, classOrStyle/>

У шаблонах FreeMarker у formHiddenInput та formPasswordInput фактично немає потреби, тому що ти можеш використовувати звичайний макрос formInput, якщо вкажеш hidden або password як значення параметра fieldType.

Параметри будь-якого з вищезгаданих макросів мають зрозуміле значення:

  • path: ім'я поля для прив'язки (тобто. "command.name")

  • options: Map всіх доступних значень, які можна обрати в полі введення. Ключі Map є значеннями, які передаються назад з форми і прив'язуються до об'єкта команди. Об'єкти Map, що зберігаються поряд із ключами, є мітками, що відображаються у формі для користувача, і можуть відрізнятися від відповідних значень, що повертаються формою. Зазвичай така Map надається контролером як довідкові дані. Ти можеш використовувати будь-яку реалізацію Map в залежності від логіки роботи. Для строго відсортованих Map можна використовувати SortedMap (наприклад, TreeMap) з відповідним Comparator, а для довільних Map, які повинні повертати значення в порядку вставки, використовуй LinkedHashMap або LinkedMap з commons-collections.

  • separator: якщо кілька опцій доступні у вигляді окремих елементів (перемикачі або прапорці), цей параметр представляє послідовність символів, що використовуються для поділу кожного з них у списку (наприклад, <br>).

  • attributes: додатковий рядок довільних тегів або тексту для включення в сам HTML-тег. Цей рядок буквально повторюється макросом. Наприклад, у полі textarea можна передати атрибути (наприклад, 'rows="5" cols="60"') або інформацію про стиль, наприклад, 'style="border:1px solid silver"'.

  • classOrStyle: для макросу showErrors – ім'я класу CSS, який використовується в елементі span, обгортаючи кожну помилку. Якщо інформація не надана (або значення порожнє), помилки обертаються в теги <b></b>.

У наступних розділах наведено приклади макросів.

Поля введення

Макро formInput приймає параметр path(command.name) та додатковий параметр attributes (який у наступному прикладі порожній). Цей макрос, як і інші макроси генерації форм, виконує неявну прив'язку Spring до параметра "path". Прив'язка залишається дійсною доти, доки не буде здійснено нову прив'язку, тому макросу showErrors не потрібно знову передавати параметр "шляху" — він працює з полем, для якого прив'язка була створена востаннє.

Макрос showErrors приймає параметр-розділювач (символи, які використовуються для розділення кількох помилок у даному полі), а також приймає другий параметр: цього разу — ім'я класу або атрибут стилю. Зверни увагу, що FreeMarker може встановити значення за замовчуванням для параметра "attributes". У наступному прикладі показано, як використовувати макроси formInput та showErrors:

<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>

У наступному прикладі показано виведення фрагменту форми з генерацією поля імені та виведенням помилки валідації після того, як форму було відправлено без значення в полі. Валідація відбувається через фреймворк Validation у Spring.

Створюваний HTML схожий на наступний приклад:

Name:
<input type="text" name="name" value="">
<br>
    <b>required</b>
<br>
<br>

Макрос formTextarea працює так само, як і макрос formInput, і приймає той самий перелік параметрів. Зазвичай другий параметр (attributes) використовується для передачі інформації про стиль або атрибути rows і cols для textarea.

Поля вибору

Ти можеш використовувати чотири макроси для полів вибору, щоб генерувати звичайні вхідні дані вибору значень інтерфейсу користувача у своїх HTML-формах :

  • formSingleSelect

  • formMultiSelect

  • formRadioButtons

  • formCheckboxes

Кожен із чотирьох макросів приймає Map опцій, яка містить значення для поля форми та мітку, що відповідає цьому значенню. Значення та мітка можуть бути однаковими.

Наступний приклад стосується перемикачів у FTL. Базовий об'єкт форми задає для цього поля значення за замовчуванням "London", тому валідація не потрібна. Коли форма відображається, весь список міст для вибору надається як довідкові дані в моделі під ім'ям "cityMap". У наступному лістингу показаний приклад:

...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>

У попередньому лістингу відображається рядок перемикачів, по одному для кожного значення cityMap, і використовується роздільник "". Жодних додаткових атрибутів не привласнюється (останнього параметра макросу немає). cityMap використовує ту саму String для кожної пари ключ-значення в Map. Ключі Map — це те, що форма фактично надсилає як параметри POST-запиту. Значення Map — це позначки, які бачить користувач. У попередньому прикладі, враховуючи список із трьох відомих міст і значення за замовчуванням у базовому об'єкті форми, HTML виглядає так:


Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>

Якщо твоя програма передбачає роботу з містами за внутрішніми кодами (наприклад), то можна створити Map кодів з відповідними ключами, як показано в наступному прикладі:

Java

protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
    Map<String, String> cityMap = new LinkedHashMap<>();
    cityMap.put("LDN", "London");
    cityMap.put("PRS", "Paris");
    cityMap.put("NYC", "New York");
    Map<String, Object> model = new HashMap<>();
    model.put("cityMap", cityMap);
    return model;
}
Kotlin

protected fun referenceData(request: HttpServletRequest): Map<String, *> {
    val cityMap = linkedMapOf(
            "LDN" to "London",
            "PRS" to "Paris",
            "NYC" to "New York"
    )
    return hashMapOf("cityMap" to cityMap)
}

Тепер код виводить дані, де значеннями перемикача є відповідні коди, але користувач, як і раніше, бачить зручніші назви міст, як показано нижче:


Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>

HTML-екранування

Використання за замовчуванням макросів форми, описаних раніше, призводить до створення HTML-елементів, які відповідають стандарту HTML 4.01 і використовують значення за замовчуванням для HTML-екранування, визначене у файлі web.xml, яке використовується засобами підтримки прив'язки Spring. Щоб зробити елементи сумісними з XHTML або перевизначити значення HTML-екранування за замовчуванням, можна вказати дві змінні у твоєму шаблоні (або у твоїй моделі, де їх бачать твої шаблони). Перевага їх зазначення в шаблонах полягає в тому, що їх можна змінити на інші значення пізніше під час обробки шаблону, щоб забезпечити різну логіку роботи для різних полів твоєї форми. Щоб перейти до режиму сумісності з XHTML для твоїх тегів, встанови значення true для моделі або контекстної змінної з ім'ям xhtmlCompliant, як показано в наступному прикладі:


<#-- для FreeMarker -->
<#assign xhtmlCompliant = true>

Після обробки цієї директиви всі елементи, створені макросами Spring, будуть сумісні зі стандартом XHTML.

Аналогічним чином можна встановити HTML-екранування для кожного поля, як показано в наступному прикладі:


<#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML-екранування off -->