1. 概述
Mustache属于无逻辑模板引擎,因为其不支持if-else和for语句,主要是有{{}}括起来的模板变量及包含模板数据的模型对象组成,因为双括号看起来像胡子,因此得名mustache
模板支持多种语言的客户端和服务器,当然也可以使用java库解析模板,需要引入相应依赖:
Java 8+
com.github.spullara.mustache.java
compiler
0.9.4
Java 6/7:
com.github.spullara.mustache.java
compiler
0.8.18
2. 实战使用
示例需求如下:
写个简单模板
使用Java Api编译模板
提供必要的数据生成动态内容
2.1. 简单模板
下面模板用于显示代办任务信息,命名为todo.mustache:
Created on {{createdOn}}
{{text}}
在{{}}中的模板变量可以是Java类的方法和属性,也是Map对象的key。
2.2. 编译模板
编译模板代码:
MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("todo.mustache");
MustacheFactory 在类路径下搜索模板文件,我们的模板文件在src/main/resources路径下。
2.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();
3. Mustache的节(Section)和迭代
下面看如何列出所有代办事项,即迭代列表数据。这需要使用Mustache的节(Section),节是根据上下文中key的值决定重复一次货多次的代码块。
示例如下:
{{#todo}}
{{/todo}}
节以#号开头,/结尾,其中的变量会被解析客户支持http://www.fx61.com/support用于渲染实际内容。下面介绍依据键的值可能遇到的场景。
3.1. 非空列表或非假值
首先定义 todo-section.mustache 模板:
{{#todo}}
Created on {{createdOn}}
{{text}}
{{/todo}}
来看看解析动作:
public void givenTodoObject_whenGetHtml_thenSuccess()
throws IOException {
Todo todo = new Todo("Todo 1", "Todo description");
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todo.mustache");
Map context = new HashMap<>();
context.put("todo", todo);
String expected = "
assertThat(executeTemplate(m, todo)).contains(expected);
}
我们在看看另一个模板:
{{#todos}}
{{/todos}}
使用代办列表数据进行测试:
public void givenTodoList_whenGetHtml_thenSuccess()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos.mustache");
List todos = Arrays.asList(
new Todo("Todo 1", "Todo description"),
new Todo("Todo 2", "Todo description another"),
new Todo("Todo 3", "Todo description another")
);
Map context = new HashMap<>();
context.put("todos", todos);
assertThat(executeTemplate(m, context))
.contains("
.contains("
.contains("
}
3.2. 空列表、假值或Null值
首先测试null值:
public void givenNullTodoObject_whenGetHtml_thenEmptyHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory().compile("todo-section.mustache");
Map context = new HashMap<>();
assertThat(executeTemplate(m, context)).isEmpty();
}
同样使用空列表测试todos.mustache :
public void givenEmptyList_whenGetHtml_thenEmptyHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos.mustache");
Map context = new HashMap<>();
assertThat(executeTemplate(m, context)).isEmpty();;
}
3.3. 条件表达式
else节(inverted section)用于当上下文变量值为false、null或空列表时渲染一次,类似于if...else...,但else部分只执行一次。
使用^符号开始,/结束:
{{#todos}}
{{/todos}}
{{^todos}}
No todos!
{{/todos}}
使用空列表进行测试:
public void givenEmptyList_whenGetHtmlUsingInvertedSection_thenHtml()
throws IOException {
Mustache m = MustacheUtil.getMustacheFactory()
.compile("todos-inverted-section.mustache");
Map context = new HashMap<>();
assertThat(executeTemplate(m, context).trim())
.isEqualTo("
No todos!
");}
3.4. lambda表达式
模板中变量的值可以来自函数或lambda表达式。下面示例传入lambda表达式:
首先定义todos-lambda.mustache模板:
{{#todos}}
{{/todos}}
然后再Todo类中增加一个函数:
public Function handleDone() {
return (obj) -> done ?
String.format("Done %s minutes ago", obj) : "";
}
最终生成内容为:
完整测试代码为:
import com.github.mustachejava.Mustache;
import org.junit.Test;
import java.io.IOException;
import java.io.StringWriter;
import java.time.Instant;
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
class MustacheTest {
private String executeTemplate(Mustache m, Map context) throws IOException {
StringWriter writer = new StringWriter();
m.execute(writer, context).flush();
return writer.toString();
}
@Test
public void givenTodoList_whenGetHtmlUsingLamdba_thenHtml() throws IOException {
Mustache m = MustacheUtil.getMustacheFactory().compile("todos-lambda.mustache");
List todos = Arrays.asList(
new Todo("Todo 1", "Todo description"),
new Todo("Todo 2", "Todo description another"),
new Todo("Todo 3", "Todo description another")
);
todos.get(2).setDone(true);
todos.get(2).setCompletedOn(Date.from(Instant.now().plusSeconds(300)));
Map context = new HashMap<>();
context.put("todos", todos);
assertThat(executeTemplate(m, context).trim()).contains("Done 5 minutes ago");
}
}
工具类MustacheUtil代码:
public class MustacheUtil {
private MustacheUtil(){}
private static final MustacheFactory MF = new DefaultMustacheFactory();
public static MustacheFactory getMustacheFactory(){
return MF;
}
}
本文介绍了如何使用mustache 模板引擎,包括条件语法和lambda表达式,以及如何使用Java Api编译、渲染模板并生成目标内容