1. Обзор
В этой статье мы сосредоточимся на шаблонах Mustache и воспользуемся одним из его API-интерфейсов Java для создания динамического HTML-контента.
Mustache — это нелогичный механизм шаблонов для создания динамического контента , такого как HTML, файлов конфигурации и прочего.
2. Введение
Проще говоря, движок классифицируется как нелогичный
, потому что в нем нет конструкций, поддерживающих операторы if-else и циклы for.
Шаблоны Mustache состоят из имен тегов, окруженных { { } } (которые напоминают усы — отсюда и название), и поддерживаются объектом модели, содержащим данные для шаблона.
3. Зависимость от Maven
Компиляция и выполнение шаблонов поддерживаются несколькими языками — как на стороне клиента, так и на стороне сервера.
Чтобы иметь возможность обрабатывать шаблоны из Java, мы используем его библиотеку Java, которую можно добавить как зависимость от Maven.
Ява 8+:
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.4</version>
</dependency>
Ява 6/7:
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.8.18</version>
</dependency>
Мы можем проверить последние версии библиотеки в Central Maven Repository .
4. Использование
Давайте рассмотрим простой сценарий, который показывает, как:
- Напишите простой шаблон
- Скомпилируйте шаблон с помощью Java API
- Выполните его, предоставив необходимые данные
4.1. Простой шаблон усов
Мы собираемся создать простой шаблон для отображения сведений о задаче todo:
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
В приведенном выше шаблоне поля в фигурных скобках ({{}}) могут быть:
- методы и свойства класса Java
- ключи объекта
Map
4.2. Компиляция шаблона усов
Мы можем скомпилировать шаблон, как показано ниже:
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("todo.mustache");
MustacheFactory
ищет данный шаблон в пути к классам. В нашем примере мы поместили todo.mustache
в src/main/resources
.
4.3. Выполнение шаблона усов
Данные, предоставленные шаблону, будут экземпляром класса Todo
, определение которого:
public class Todo {
private String title;
private String text;
private boolean done;
private Date createdOn;
private Date completedOn;
// constructors, getters and setters
}
Скомпилированный шаблон можно выполнить для получения HTML, как показано ниже:
Todo todo = new Todo("Todo 1", "Description");
StringWriter writer = new StringWriter();
m.execute(writer, todo).flush();
String html = writer.toString();
5. Секции усов и итерации
Давайте теперь посмотрим, как составить список задач. Для перебора данных списка мы используем разделы Mustache.
Раздел — это блок кода, который повторяется один или несколько раз в зависимости от значения ключа в текущем контексте.
Это выглядит примерно так:
{{#todo}}
<!-- Other code -->
{{/todo}}
Раздел начинается с фунта (#) и заканчивается косой чертой (/), где за каждым из знаков следует ключ, значение которого используется в качестве основы для отображения раздела.
Ниже приведены сценарии, которые могут возникнуть в зависимости от значения ключа:
5.1. Раздел с непустым списком или неложным значением
Давайте создадим шаблон todo-section.mustache
, который использует раздел:
{{#todo}}
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
{{/todo}}
Давайте посмотрим на этот шаблон в действии:
@Test
public void givenTodoObject_whenGetHtml_thenSuccess()
throws IOException {
Todo todo = new Todo("Todo 1", "Todo description");
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todo.mustache");
Map<String, Object> context = new HashMap<>();
context.put("todo", todo);
String expected = "<h2>Todo 1</h2>";
assertThat(executeTemplate(m, todo)).contains(expected);
}
Давайте создадим еще один шаблон todos.mustache
для перечисления задач:
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
И создайте список задач, используя его:
@Test
public void givenTodoList_whenGetHtml_thenSuccess()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos.mustache");
List<Todo> todos = Arrays.asList(
new Todo("Todo 1", "Todo description"),
new Todo("Todo 2", "Todo description another"),
new Todo("Todo 3", "Todo description another")
);
Map<String, Object> context = new HashMap<>();
context.put("todos", todos);
assertThat(executeTemplate(m, context))
.contains("<h2>Todo 1</h2>")
.contains("<h2>Todo 2</h2>")
.contains("<h2>Todo 3</h2>");
}
5.2. Раздел с пустым списком
или ложным
или нулевым
значением
Давайте проверим todo-section.mustache
с нулевым
значением:
@Test
public void givenNullTodoObject_whenGetHtml_thenEmptyHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todo-section.mustache");
Map<String, Object> context = new HashMap<>();
assertThat(executeTemplate(m, context)).isEmpty();
}
Аналогичным образом протестируйте todos.mustache
с пустым списком:
@Test
public void givenEmptyList_whenGetHtml_thenEmptyHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos.mustache");
Map<String, Object> context = new HashMap<>();
assertThat(executeTemplate(m, context)).isEmpty();;
}
6. Перевернутые секции
Инвертированные разделы — это те, которые отображаются только один раз на основе отсутствия ключа, ложного
или нулевого
значения или пустого списка. Другими словами, они отображаются, когда раздел не отображается.
Они начинаются с символа вставки (^) и заканчиваются косой чертой (/), как показано ниже:
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
{{^todos}}
<p>No todos!</p>
{{/todos}}
Приведенный выше шаблон при наличии пустого списка:
@Test
public void givenEmptyList_whenGetHtmlUsingInvertedSection_thenHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos-inverted-section.mustache");
Map<String, Object> context = new HashMap<>();
assertThat(executeTemplate(m, context).trim())
.isEqualTo("<p>No todos!</p>");
}
7. Лямбды
Значения ключей секции усов могут быть функцией или лямбда-выражением . В таком случае полное лямбда-выражение вызывается путем передачи текста в разделе в качестве параметра лямбда-выражению.
Давайте посмотрим на шаблон todos-lambda.mustache
:
{{#todos}}
<h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2>
{{/todos}}
Ключ handleDone преобразуется
в лямбда-выражение Java 8, как показано ниже:
public Function<Object, Object> handleDone() {
return (obj) -> done ?
String.format("<small>Done %s minutes ago<small>", obj) : "";
}
HTML-код, созданный при выполнении вышеуказанного шаблона:
<h2>Todo 1</h2>
<h2>Todo 2</h2>
<h2>Todo 3<small>Done 5 minutes ago<small></h2>
8. Заключение
В этой вводной статье мы рассмотрели создание шаблонов усов с разделами, перевернутыми разделами и лямбда-выражениями. И мы использовали API Java для компиляции и выполнения шаблонов, предоставляя соответствующие данные.
Есть еще несколько расширенных функций Mustache, которые стоит изучить, например:
- предоставление вызываемого объекта в качестве значения, которое приводит к параллельной оценке
- использование
DecoratedCollection
для получения первого, последнего и индекса элементов коллекции инвертировать
API, который дает данные с учетом текста и шаблона
И, как всегда, полный исходный код доступен на Github .