Mustache is a logicless template engine and it is helpful for creating dynamic content like HTML and configuration files. Logicless means it does not have a structured flow with if else/for loops/while loops etc., It just contains tag names surrounded by { { } } and hence they are called Mustache templates(that resembles a mustache). A model object is required which contains the data to be filled in the template. Let us see the functionality via a maven project. For that let us see the Maven dependency first.
Maven dependency:
Multiple languages can use Compilation and execution of the templates. Languages can be both on the client side and server side. Let us use a Java library that can be added as a Maven dependency.
For Java 8:
<dependency> <groupId>com.github.spullara.mustache.java</groupId> <artifactId>compiler</artifactId> <version>0.9.4</version> </dependency>
For Java 6/7:
<dependency> <groupId>com.github.spullara.mustache.java</groupId> <artifactId>compiler</artifactId> <version>0.8.18</version> </dependency>
Example Project
Project Structure:
As it is a maven project. let us see pom.xml
pom.xml
XML
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 < modelVersion >4.0.0</ modelVersion > < artifactId >mustache</ artifactId > < name >mustache</ name > < packaging >jar</ packaging > < parent > < groupId >com.gfg</ groupId > < artifactId >parent-boot-2</ artifactId > < version >0.0.1-SNAPSHOT</ version > < relativePath >../parent-boot-2</ relativePath > </ parent > < dependencies > < dependency > < groupId >com.github.spullara.mustache.java</ groupId > < artifactId >compiler</ artifactId > < version >${mustache.compiler.api.version}</ version > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-mustache</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > < dependency > < groupId >org.webjars</ groupId > < artifactId >bootstrap</ artifactId > < version >${webjars.bootstrap.version}</ version > </ dependency > < dependency > < groupId >org.fluttercode.datafactory</ groupId > < artifactId >datafactory</ artifactId > < version >${datafactory.version}</ version > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-maven-plugin</ artifactId > </ plugin > </ plugins > </ build > < properties > < mustache.compiler.api.version >0.9.10</ mustache.compiler.api.version > < datafactory.version >0.8</ datafactory.version > < webjars.bootstrap.version >3.3.7</ webjars.bootstrap.version > </ properties > </ project > |
Let us see how to create a Simple template
<h2>{{title}}</h2> <small>Created on {{createdOn}}</small> <p>{{text}}</p>
This template will be helpful to display the details that are present in TodoItems. ({{}}) is mainly helpful for getting
- Methods and Properties of a Java class
- Keys of a Map object
For compiling the Mustache Template, we need to use MustacheFactory. Here in our program, it searches for the template that is present in classpath, and here it is available at src/main/resources. For compiling the template
MustacheFactory mustacheFactory = new DefaultMustacheFactory(); Mustache mustache = mustacheFactory.compile("todoitems.mustache");
Example of a template file
<div class="starter-template"> {{#articles}} <h1>{{title}}</h1> <h3>{{publishDate}}</h3> <h3>{{author}}</h3> <p>{{body}}</p> {{/articles}} </div>
Let us see a model file that produces the data
TodoItems.java
Java
import java.time.Duration; import java.util.Date; import java.util.function.Function; public class TodoItems { public TodoItems() { } public TodoItems(String title, String text) { this .itemTitle = title; this .itemText = text; createdDate = new Date(); } private String itemTitle; private String itemText; private boolean done = false ; private Date createdDate; private Date completedDate; public void markAsDone() { done = true ; completedDate = new Date(); } public String getTitle() { return itemTitle; } public void setTitle(String title) { this .itemTitle = title; } public String getText() { return itemText; } public void setText(String text) { this .itemText = text; } public boolean getDone() { return done; } public Date getCreatedOn() { return createdDate; } public Date getCompletedOn() { return completedDate; } public void setDone( boolean done) { this .done = done; } public void setCompletedOn(Date completedOn) { this .completedDate = completedOn; } public long doneSince() { return done ? Duration .between(createdDate.toInstant(), completedDate.toInstant()) .toMinutes() : 0 ; } public Function<Object, Object> handleDone() { return (obj) -> done ? String.format( "<small>Done %s minutes ago<small>" , obj) : "" ; } } |
The compiled template which produces HTML is as follows
TodoItems todoItems = new TodoItems("Todo 1", "Description"); StringWriter writer = new StringWriter(); m.execute(writer, todoItems).flush(); String html = writer.toString();
Not only sequential data, but the list of data also, but it can also be supported via
{{#todo}} <!-- Other code --> {{/todo}}
The important thing that needs to be noted is the # and/or which represents the basis for rendering the section. As a spring project, the data rendering also happening. Hence let us see the key files to execute it
MustacheUtil.java
Java
import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.MustacheFactory; public class MustacheUtil { private static final MustacheFactory mf = new DefaultMustacheFactory(); public static MustacheFactory getMustacheFactory(){ return mf; } } |
SpringMustacheApplication.java
Java
import com.samskivert.mustache.DefaultCollector; import com.samskivert.mustache.Mustache; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan (basePackages = { "com.gfg" }) public class SpringMustacheApplication { public static void main(String[] args) { SpringApplication.run(SpringMustacheApplication. class , args); } @Bean public Mustache.Compiler mustacheCompiler(Mustache.TemplateLoader templateLoader) { return Mustache.compiler() // Here if the value is not provided // then defaultValue will be taken .defaultValue( "Some Author" ) .withLoader(templateLoader) .withCollector( new DefaultCollector()); } } |
GeekArticleController.java
Java
import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.fluttercode.datafactory.impl.DataFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; import com.gfg.springmustache.model.GeekArticle; @Controller public class GeekArticleController { @GetMapping ( "/article" ) public ModelAndView displayArticle(Map<String, Object> model) { List<GeekArticle> geekArticles = IntStream.range( 0 , 10 ) .mapToObj(i -> generateArticle( "Article Title " + i)) .collect(Collectors.toList()); model.put( "articles" , geekArticles); return new ModelAndView( "index" , model); } private GeekArticle generateArticle(String title) { GeekArticle geekArticle = new GeekArticle(); DataFactory factory = new DataFactory(); geekArticle.setTitle(title); geekArticle.setBody( "A data structure is not only used for organizing the data. It is also used for processing, retrieving, and storing data. There are different basic and advanced types of data structures that are used in almost every program or software system that has been developed. So we must have good knowledge about data structures." ); geekArticle.setPublishDate(factory.getBirthDate().toString()); //article.setAuthor(factory.getName()); return geekArticle; } } |
GeekArticle.java
Java
public class GeekArticle { private String geekTitle; private String geekBody; private String geekAuthorName; private String publishDate; public String getTitle() { return geekTitle; } public void setTitle(String title) { this .geekTitle = title; } public String getBody() { return geekBody; } public void setBody(String body) { this .geekBody = body; } public String getAuthor() { return geekAuthorName; } public void setAuthor(String author) { this .geekAuthorName = author; } public String getPublishDate() { return publishDate; } public void setPublishDate(String publishDate) { this .publishDate = publishDate; } } |
Let us see the resources folder html key files
geekarticle.html
HTML
< div class = "starter-template" > <!-- Using the list pattern Begin --> {{#articles}} < h1 >{{title}}</ h1 > < h3 >{{publishDate}}</ h3 > < h3 >{{author}}</ h3 > < p >{{body}}</ p > <!-- Using the list pattern End --> {{/articles}} </ div > |
index.html
HTML
{{>layout/header}} < body > < div class = "container" >{{>layout/geekarticle}}</ div > < script type = "text/javascript" src = "webjars/bootstrap/3.3.7/js/bootstrap.min.js" ></ script > </ body > {{>layout/footer}} |
todo.mustache
<h2>{{title}}</h2> <small>Created on {{createdOn}}</small> <p>{{text}}</p>
Let us run the application as a spring boot project. The console output is shown here
On execution of the controller, we can see the below output
For producing inverted sections, we need to follow the pattern below
{{#todos}} <h2>{{title}}</h2> {{/todos}} {{^todos}} // start with a caret (^) and end with a slash (/) <p>No todos!</p> {{/todos}}
Conclusion
In this article, we saw creating of simple or inverted mustache templates and then java API is used to compile and execute the mustache templates by providing relevant data. It is a logicless template and is supported by many languages like Java, Python, Ruby, Android, Kotlin, etc.