FreeMarker шаблони

Apache FreeMarker — це механізм шаблонів: бібліотеки Java для генерації текстового виводу (HTML-сторінки, xml, конфігураційні файли, вихідний код і. т. д. На вхід подається шаблон, наприклад html в якому є спеціальні вирази, підготовляються дані відповідні цим виразом, а Freemarker динамічно вставляє ці дані і виходить динамічно заповнений документ.

У статті FreeMarker
Spring boot
Macros
REST API

Тобто простий вираз на freemarker це наприклад ${name}, вираження підтримуються обчислення, операції порівняння, умови, цикли, списки, вбудовані функції, макроси і багато ін. Приклад html з виразом ${name} (шаблон test.ftl)

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
<title>${name}!</title>
</head>
<body>
 <h2>Hello ${name}!</h2> 
</body>
</html>

Якщо тепер створити в java модель даних

import freemarker.template.Configuration;
import freemarker.template.Template;
...
// Конфігурація
Configuration cfg = new Configuration(Конфігурація.VERSION_2_3_27);
// модель даних
Map<String, Object> root = new HashMap<>();
root.put("name", "Freemarker");
// шаблон
Template temp = cfg.getTemplate("test.ftl");
// обробка шаблону і моделі даних
Writer out = new OutputStreamWriter(System.out);
// вивід на консоль
temp.process(root, out);

то отримаємо html-документ з заповненим name.
Якщо треба обробити список, то використовується конструкція #list, наприклад для html списку

<ul>
 <#list father as item>
<li>${item}</li>
</#list>
</ul>

В java, в модель даних подати список можна так
Map<String, Object> root = new HashMap<>();
....
root.put("father", Arrays.asList("Alexander", "Petrov", 47));

Перейдемо до Spring

В Spring boot є підтримка Freemarker. На сайті SPRING INITIALIZR можна отримати pom файл проекту.
pom файл

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>demoFreeMarker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demoFreeMarker</name>
 <description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
 <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.звітування.outputEncoding>UTF-8</project.звітування.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>

клас DemoFreeMarkerApplication

@SpringBootApplication
public class DemoFreeMarkerApplication {

 public static void main(String[] args) {
 SpringApplication.run(DemoFreeMarkerApplication.class, args);
}
}

В Spring є вже підготовлений компонент конфігурації Configuration для freemarker.
Для прикладу консольного додатка візьму spring інтерфейс для обробки командного рядка(CommandLineRunner) і підготую модель даних для наступного шаблону ftl (hello_test.ftl)
Шаблон hello_test.ftl

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Hello ${name}!</title>
</head>
<body>

<input type="text" placeholder="${name}">

<table>
 <#list persons as row>
<tr>
 <#list row as field>
<td>${field}</td>
</#list>
</tr>
</#list>
</table>

</body>
</html>

Java-код для моделі даних шаблону hello_test.ftl
клас CommandLine і модель даних

@Component
public class CommandLine implements CommandLineRunner {

@Autowired
 private Configuration configuration;

 public void run(String... args) {
 Map<String, Object> root = new HashMap<>();
 // для ${name}
 root.put("name", "Fremarker");
 // для <#list persons
 List<List> persons = new ArrayList<>();
 persons.add(Arrays.asList("Alexander", "Petrov", 47));
 persons.add(Arrays.asList("Slava", "Petrov", 13));
 root.put("persons", persons);

 try {
 Template template = configuration.getTemplate("hello_test.ftl");
 Writer out = new OutputStreamWriter(System.out);
 try {
 template.process(root, out);
 } catch (TemplateException e) {
e.printStackTrace();
}
 } catch (IOException e) {
e.printStackTrace();
}
}
}

Після обробки отримаємо html документ
Output html

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Hello Fremarker!</title>
</head>
<body>

<input type="text" placeholder="Fremarker">

<table>
<tr>
<td>Alexander</td>
<td>Petrov</td>
<td>47</td>
</tr>
<tr>
<td>Slava</td>
<td>Petrov</td>
<td>13</td>
</tr>
</table>
</body>

 

Читайте також  Експрес-огляд продуктивності PostgreSQL 10.5 в новітніх хмарних сервісах Яндекс.Хмари

Макроси

У freemarker є підтримка макросів, це дуже зручна і сильна його сторона і використовувати її просто необхідно.
Простий приклад:

<#macro textInput id value="">
 <input type="text" id="${id}" value="${value}">
</#macro>

Це макрос з ім’ям textInput і параметрами id (він обов’язковий) і value (він не обов’язковий, т. к. має значення за замовчуванням). Далі йде його тіло і використання вхідних параметрів. В шаблоні файл з макросами підключається так:

<#import "ui.ftl" as ui/>

З шаблону макрос викликається так:

<@ui.textInput id="name" value="${name}"/>

Де ui це аліас який вказали при підключенні, ${name} змінна моделі, далі через аліас посилаємося на ім’я макросу textInput і вказуємо його параметри, як мінімум обов’язкові. Підготую прості макроси для html Input і Table
файл макросів ui.ftl

<#-- textInput macro for html --input>
<#macro textInput id placeholder="" value="">
 <input type="text" id="${id}" placeholder="${placeholder}" value="${value}">
</#macro>

<#-- table macro for html table -->
<#macro table id rows>
<table id="${id}">
 <#list rows as row>
<tr>
 <td>${row?index + 1}</td>
 <#list row as field>
<td>${field}</td>
</#list>
</tr>
</#list>
</table>
</#macro>

${row?index + 1} це вбудована підтримка індексу елемента списку, таких вбудованих функцій багато. Якщо тепер змінити попередній основний шаблон і замінити в ньому input і table на макроси, то вийде такий документ

Шаблон hello.ftl

<#import "ui.ftl" as ui/>

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Hello ${name}!</title>
</head>
<body>

<@ui.textInput id="name placeholder="Enter name" value="${name}"/>
<@ui.table id="table1" rows=persons/>

</body>
</html>

 

REST

Звичайно таку модель зручно використовувати у web додатку. Підключаю залежність в pom

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Додаю REST Controller
DemoController.java

@Controller
public class DemoController {

@Autowired
 private RepositoryService repositoryService;

@GetMapping("/")
 public String index() {
 return "persons";
}

 @RequestMapping(value = "/search", method = RequestMethod.POST)
 public String hello(Model model, @RequestParam(defaultValue = "") String searchName) {
 List<List<String>> persons = repositoryService.getRepository();
 List<List<String>> filterList = persons.stream()
 .filter(p -> p.get(0).contains(searchName))
.collect(Collectors.toList());
 model.addAttribute("persons", filterList);
 model.addAttribute("lastSearch", searchName);
 return "persons";
}

 @RequestMapping(value = "/save", method = RequestMethod.POST)
 public String save(Model model, @ModelAttribute("person") Person person) {
 List<List<String>> persons = repositoryService.addPerson(person);
 model.addAttribute("persons", persons);
 return "persons";
}
}

Service репозиторій для осіб
RepositoryService.java

@Service
public class RepositoryService {

 private static List<List<String>> repository = new ArrayList<>();

 public List<List<String>> getRepository() {
 return repository;
}

 public List<List<String>> addPerson(Person person) {
 repository.add(Arrays.asList(person.getFirstName(), person.getAge().toString()));
 return repository;
}
}

Клас особа
Person.java

public class Person {

 public Person(String firstName, Integer age) {
 this.firstName = firstName;
 this.age = age;
}

 private String firstName;
 private Integer age;

 public String getFirstName() {
 return firstName;
}

 public Integer getAge() {
 return age;
}
}

Шаблон макросів
ui.ftl

<#macro formInput id name label type="text" value="">
<label for="${id}">${label}</label>
<input type="${type}" id="${id}" name="${name}" value="${value}">
</#macro>

<#macro table id rows>
<table id="${id}" border="1px" cellspacing="2" border="1" cellpadding="5">
 <#list rows as row>
<tr>
 <td>${row?index + 1}</td>
 <#list row as field>
<td>${field}</td>
</#list>
</tr>
</#list>
</table>
</#macro>

Основний шаблон
persons.ftl

<#import "ui.ftl" as ui/>

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
<title>Person</title>
 <link href="style/my.css" rel="stylesheet">
</head>
<body>

<div>
<fieldset>
 <legend>Додати особа</legend>
 <form name="person" action="save" method="POST">
 <@ui.formInput id="t1" name="firstName" label="Ім'я"/> <br/>
 <@ui.formInput id="t2" name="age" label="Вік"/> <br/>
 <input type="submit" value="Save" />
</form>
</fieldset>
</div>

<div>
<fieldset>
<legend>Пошук</legend>
 <form name="searchForm" action="search" method="POST">
 <@ui.formInput id="t3" name="searchName" label="Пошук"/> <br/>
 <input type="submit" value="Search" />
</form>
</fieldset>
</div>
<p><#if lastSearch??>Пошук: ${lastSearch}<#else></#if></p>

<@ui.table id="table1" rows=persons![]/>

</body>
</html>

Структура проекту

Читайте також  Firefox можливо буде монетизуватися за рахунок вбудованого платного VPN-доповнення

Додаток буде обробляти дві команди «save» і «search» особи (див. контролер). Всю роботу по обробці (мапінгу) вхідних параметрів, що бере на себе Spring.

Деякі пояснення до шаблону.
<#if lastSearch??>Пошук: ${lastSearch}<#else></#if>
тут перевіряється, якщо параметр заданий, то вивести фразу «Пошук для: ..», інакше нічого
<@ui.table id="table1" rows=persons![]/>
тут теж зроблена перевірка, що список осіб присутній, інакше порожній. Ці перевірки важливі при першому відкритті сторінки, інакше довелося б їх ініціалізувати в index(), контролера.

Робота програми

Матеріали:
Apache FreeMarker Manual

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *