1. Overview
In this article, we’ll focus on Mustache templates and use one of its Java APIs for producing dynamic HTML content.
Mustache is a logicless template engine for creating dynamic content like HTML, configuration files among other things.
2. Introduction
Simply put, the engine is classified as logicless because it doesn’t have constructs that support if-else statements and for loops.
The Mustache templates consist of tag names surrounded by { { } } (which resemble mustaches – hence the name) and are backed by a model object containing the data for the template.
3. Maven Dependency
Compiling and execution of the templates is supported by multiple languages – both client side and server side.
To be able to process the templates from Java we make use of its Java library which can be added as a Maven dependency.
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>
We can check the latest versions of the library in the Central Maven Repository.
4. Usage
Let’s look at a simple scenario which shows how to:
- Write a simple template
- Compile the template using Java API
- Execute it by providing the necessary data
4.1. A Simple Mustache Template
We’re going to create a simple template for displaying the details of a todo task:
<h2>{{title}}</h2> <small>Created on {{createdOn}}</small> <p>{{text}}</p>
In the above template the fields within the curly braces ({{}}) can be:
- methods and properties of a Java class
- keys of a Map object
4.2. Compiling the Mustache Template
We can compile the template as shown below:
MustacheFactory mf = new DefaultMustacheFactory(); Mustache m = mf.compile("todo.mustache");
MustacheFactory searches for the given template in the classpath. In our example, we place todo.mustache under src/main/resources.
4.3. Executing the Mustache Template
The data provided to the template will be an instance of the Todo class which definition is:
public class Todo { private String title; private String text; private boolean done; private Date createdOn; private Date completedOn; // constructors, getters and setters }
The compiled template can be executed to get HTML as shown below:
Todo todo = new Todo("Todo 1", "Description"); StringWriter writer = new StringWriter(); m.execute(writer, todo).flush(); String html = writer.toString();
5. Mustache Sections and Iterations
Let’s now have a look at how to list the todos. For iterating over a list data, we make use of Mustache sections.
A section is a block of code which is repeated one or more times depending on the value of the key in the current context.
It looks something like:
{{#todo}} <!-- Other code --> {{/todo}}
A section begins with a pound (#) and ends with a slash (/), where each of the signs is followed by the key whose value is used as the basis for rendering the section.
Following are the scenarios that can occur depending on the value of the key:
5.1. Section with Non-Empty List or Non-False Value
Let’s create a template todo-section.mustache which uses a section:
{{#todo}} <h2>{{title}}</h2> <small>Created on {{createdOn}}</small> <p>{{text}}</p> {{/todo}}
Let’s look at this template in action:
@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); }
Let’s create another template todos.mustache for listing the todos:
{{#todos}} <h2>{{title}}</h2> {{/todos}}
And create a listing of todos using it:
@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. Section With Empty List or False or Null Value
Let’s test the todo-section.mustache with a null value:
@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(); }
And likewise, test todos.mustache with an empty list:
@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
Inverted sections are those which are rendered only once based on the non-existence of the key or false or null value or an empty list. In other words, these are rendered when a section is not rendered.
These start with a caret (^) and end with a slash (/) as shown below:
{{#todos}} <h2>{{title}}</h2> {{/todos}} {{^todos}} <p>No todos!</p> {{/todos}}
The above template when provided with an empty list:
@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
The values for keys of a mustache section can be a function or a lambda expression. In such case, the complete lambda expression is invoked by passing in the text within the section as a parameter to the lambda expression.
Let’s look at a template todos-lambda.mustache:
{{#todos}} <h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2> {{/todos}}
The handleDone key resolves to a Java 8 lambda expression as shown below:
public Function<Object, Object> handleDone() { return (obj) -> done ? String.format("<small>Done %s minutes ago<small>", obj) : ""; }
The HTML generated by executing the above template is:
<h2>Todo 1</h2> <h2>Todo 2</h2> <h2>Todo 3<small>Done 5 minutes ago<small></h2>
8. Conclusion
In this introductory article, we looked at creating mustache templates with sections, inverted sections, and lambdas. And we used the Java API to compile and execute the templates by providing relevant data.
There are few more advanced features of Mustache that are worth exploring – such as:
- providing a callable as a value which results in a concurrent evaluation
- using DecoratedCollection to get first, last and index of collection elements
- invert API which gives the data given the text and the template
And, as always, the complete source code for this is available over on Github.