Mustache 模板教程

Mustache 模板教程

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

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. 实战使用

示例需求如下:

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

2.1. 简单模板

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

{ {title}}

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}}

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

3.1. 非空列表或非假值

首先定义 todo-section.mustache 模板:

{
    {#todo}}

{ {title}}

Created on { {createdOn}}

{ {text}}

{ {/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 context = new HashMap<>();
    context.put("todo", todo);
  
    String expected = "

Todo 1

"; assertThat(executeTemplate(m, todo)).contains(expected); }

我们再看看另一个模板:

{
    {#todos}}

{ {title}}

{ {/todos}}

使用代办列表数据进行测试:

@Test
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("

Todo 1

") .contains("

Todo 2

") .contains("

Todo 3

"); }

3.2. 空列表、假值或Null值

首先测试null值:

@Test
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 :

@Test
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}}

{ {title}}

{ {/todos}} { {^todos}}

No todos!

{ {/todos}}

使用空列表进行测试:

@Test
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}}

{ {title}}{ {#handleDone}}{ {doneSince}}{ {/handleDone}}

{ {/todos}}

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

public Function handleDone() {
    return (obj) -> done ? 
      String.format("Done %s minutes ago", obj) : "";
}

public long doneSince() {
   return done ? Duration
     .between(createdOn.toInstant(), completedOn.toInstant())
     .toMinutes() : 0;
}

最终生成内容为:

Todo 1

Todo 2

Todo 3Done 5 minutes ago

完整测试代码为:

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;
    }
}

4. 总结

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

你可能感兴趣的:(工具软件,Mustache)