1. 概要

この記事では、 Mustache テンプレートに焦点を当て、その JavaAPIの1つを使用してダイナミックHTMLコンテンツを作成します。

Mustacheは、HTMLや構成ファイルなどの動的コンテンツを作成するためのロジックレステンプレートエンジンです。

2. 序章

簡単に言うと、エンジンにはif-elseステートメントとforループをサポートする構造がないため、logiclessに分類されます。

Mustacheテンプレートは、 {{}} (Mustacheに似ているため、名前)で囲まれたタグ名で構成され、テンプレートのデータを含むモデルオブジェクトによってサポートされます。

3. Mavenの依存関係

テンプレートのコンパイルと実行は、クライアント側とサーバー側の両方の複数の言語でサポートされています。

Javaからテンプレートを処理できるようにするために、Maven依存関係として追加できる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>

ライブラリの最新バージョンは、CentralMavenリポジトリで確認できます。

4. 使用法

次の方法を示す簡単なシナリオを見てみましょう。

  1. 簡単なテンプレートを書く
  2. JavaAPIを使用してテンプレートをコンパイルします
  3. 必要なデータを提供して実行します

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.mustachesrc/ 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. 口ひげのセクションと反復

ToDoを一覧表示する方法を見てみましょう。 リストデータを反復処理するために、Mustacheセクションを使用します。

セクションは、現在のコンテキストでのキーの値に応じて1回以上繰り返されるコードのブロックです。

次のようになります。

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

ToDoを一覧表示するための別のテンプレートtodos.mustacheを作成しましょう。

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

そして、それを使用してToDoのリストを作成します。

@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.mustachenull値でテストしてみましょう。

@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. 反転セクション

反転セクションは、キーの存在しないfalseまたはnull値、または空のリストに基づいて1回だけレンダリングされるセクションです。 つまり、これらはセクションがレンダリングされていないときにレンダリングされます。

以下に示すように、これらはキャレット(^)で始まり、スラッシュ(/)で終わります。

{{#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 キーは、以下に示すようにJava8ラムダ式に解決されます。

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. 結論

この紹介記事では、セクション、反転セクション、ラムダを使用した口ひげテンプレートの作成について説明しました。 また、Java APIを使用して、関連データを提供することでテンプレートをコンパイルおよび実行しました。

探索する価値のあるMustacheのより高度な機能はいくつかあります。たとえば、次のようなものです。

  • 同時評価をもたらす値として呼び出し可能オブジェクトを提供する
  • DecoratedCollection を使用して、コレクション要素の最初、最後、およびインデックスを取得します
  • invert APIは、テキストとテンプレートを指定してデータを提供します

そして、いつものように、このための完全なソースコードはGithub入手できます。