Skip to main content
 首页 » 编程设计

Mustache 模板教程

2022年07月19日157tintown

Mustache 模板教程

本文学习Mustache 模板,并使用Java api动态生成HTML内容。Mustache是创建动态内的无逻辑模板引擎,如HTML,配置文件等。

1. 概述

Mustache属于无逻辑模板引擎,因为其不支持if-else和for语句,主要是有{ {}}括起来的模板变量及包含模板数据的模型对象组成,因为双括号看起来像胡子,因此得名mustache

模板支持多种语言的客户端和服务器,当然也可以使用java库解析模板,需要引入相应依赖:
Java 8+

<dependency> 
    <groupId>com.github.spullara.mustache.java</groupId> 
    <artifactId>compiler</artifactId> 
    <version>0.9.4</version> 
</dependency> 

Java 6/7:

<dependency> 
    <groupId>com.github.spullara.mustache.java</groupId> 
    <artifactId>compiler</artifactId> 
    <version>0.8.18</version> 
</dependency> 

读者也可以查找最新版本。

2. 实战使用

示例需求如下:

  1. 写个简单模板
  2. 使用Java Api编译模板
  3. 提供必要的数据生成动态内容

2.1. 简单模板

下面模板用于显示代办任务信息,命名为todo.mustache:

<h2>{
  {title}}</h2> 
<small>Created on {
  {createdOn}}</small> 
<p>{
  {text}}</p> 

在{ {}}中的模板变量可以是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}} 
<!-- Other code --> 
{
  {/todo}} 

节以#号开头,/结尾,其中的变量会被解析用于渲染实际内容。下面介绍依据键的值可能遇到的场景。

3.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}} 
<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>"); 
} 

3.2. 空列表、假值或Null值

首先测试null值:

@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();; 
} 

3.3. 条件表达式

else节(inverted section)用于当上下文变量值为false、null或空列表时渲染一次,类似于if...else...,但else部分只执行一次。
使用^符号开始,/结束:

{
  {#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>"); 
} 

3.4. lambda表达式

模板中变量的值可以来自函数或lambda表达式。下面示例传入lambda表达式:

首先定义todos-lambda.mustache模板:

{
  {#todos}} 
<h2>{
  {title}}{
  {#handleDone}}{
  {doneSince}}{
  {/handleDone}}</h2> 
{
  {/todos}} 

然后再Todo类中增加两个函数:

public Function<Object, Object> handleDone() { 
    return (obj) -> done ?  
      String.format("<small>Done %s minutes ago<small>", obj) : ""; 
} 
 
public long doneSince() { 
   return done ? Duration 
     .between(createdOn.toInstant(), completedOn.toInstant()) 
     .toMinutes() : 0; 
} 

最终生成内容为:

<h2>Todo 1</h2> 
<h2>Todo 2</h2> 
<h2>Todo 3<small>Done 5 minutes ago<small></h2> 

完整测试代码为:

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<String, Object> 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<Todo> 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<String, Object> 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; 
    } 
} 

4. 总结

本文介绍了如何使用mustache 模板引擎,包括条件语法和lambda表达式,以及如何使用Java Api编译、渲染模板并生成目标内容。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/102881478