1.概述
在本文中,我们将关注Mustache模板,并使用它的一个JavaAPI来生成动态HTML内容。Mustache是一个无逻辑的模板引擎,用于创建动态内容,如HTML、配置文件等。
2.介绍
简单地说,引擎被归类为无逻辑的,因为它没有支持if-else语句和for循环的构造。Mustache模板由 {{}} 包围的标签名称组成(类似于胡子,因此得名)。
3.Maven依赖
模板的编译和执行可由多种语言支持,包括客户端和服务器端。为了能够处理Java中的模板,我们使用了它的Java库,该库可以作为Maven依赖项添加。 Java 8+:
xml
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.9.4</version>
</dependency>
Java 6/7:
xml
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.8.18</version>
</dependency>
4.用法
让我们看看一个简单的场景,它展示了如何:
- 编写一个简单的模板;
- 使用Java API编译模板;
- 通过提供必要的数据来执行。
4.1一个简单的Mustache模板
我们将创建一个简单的模板来显示待办事项任务的详细信息:
html
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
在上面的模板中,卷曲括号({{}})中的字段可以是:
- Java类的方法和属性;
- Map对象的key
4.2编译Mustache模板
我们可以编译如下所示的模板:
java
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("todo.mustache");
MustacheFactory在类路径中搜索给定的模板。在我们的示例中,我们将todo.mustache放在src/main/resources下。
4.3执行Mustache模板
提供给模板的数据将是Todo类的一个实例,其定义为:
java
public class Todo {
private String title;
private String text;
private boolean done;
private Date createdOn;
private Date completedOn;
// constructors, getters and setters
}
可以执行编译后的模板来获得HTML,如下所示:
java
Todo todo = new Todo("Todo 1", "Description");
StringWriter writer = new StringWriter();
m.execute(writer, todo).flush();
String html = writer.toString();
Mustache Sections and Iterations
现在让我们看看如何列出待办事项,为了迭代列表数据,我们使用Mustache sections。section是一个代码块,根据当前上下文中键的值重复一次或多次。 它看起来像:
js
{{#todo}}
<!-- Other code -->
{{/todo}}
节以(#)开始,以斜线(/)结束,其中这两个符号后面都跟着一个key,该键的value是渲染section的基础。 以下是根据key的值可能的情况:
5.1具有非空列表或非假值的Section
创建一个模板todo-section.mustache,它使用一个section:
js
{{#todo}}
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
{{/todo}}
让我们看看这个模板的作用:
java
@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
js
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
并使用它创建一个待办事项列表:
java
@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列表为空或值为False或Null的Section
让我们用一个null值测试todo-section.mustache:
java
@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();
}
同样,用一个空list测试todos.mustache:
java
@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.Inverted Sections
倒置Sections是指基于不存在的key、或者key为false或null值或空列表时,仅渲染一次的部分。换言之,倒置Sections在未渲染Sections时进行渲染。 它们以插入符号(^)开头,以斜线(/)结束,如下所示:
js
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
{{^todos}}
<p>No todos!</p>
{{/todos}}
以上模板在提供空list时:
java
@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.Lambdas
mustache section的key的值可以是函数或lambda表达式。在这种情况下,通过将section中的文本作为参数传递给lambda表达式来调用完整的lambda表达式。
让我们来看一个模板todos-lambda.mustache:
js
{{#todos}}
<h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2>
{{/todos}}
handleDone的key解析为Java 8 lambda表达式,如下所示:
java
public Function<Object, Object> handleDone() {
return (obj) -> done ?
String.format("<small>Done %s minutes ago<small>", obj) : "";
}
通过执行上述模板生成的HTML是:
js
<h2>Todo 1</h2>
<h2>Todo 2</h2>
<h2>Todo 3<small>Done 5 minutes ago<small></h2>
8.结论
在这篇介绍性文章中,我们研究了创建带有section、inverted section和Lambda的mustache模板。我们使用Java API通过提供相关数据来编译和执行模板。 Mustache的一些更高级的功能值得探索,例如:
- 提供一个callable作为value,从而产生并发评估;
- 使用DecoratedCollection获取集合元素的第一个、最后一个和索引;
- 反转API,在给定文本和模板的情况下提供数据。