Monday, November 18, 2024
Google search engine
HomeLanguagesJavaHow to Generate Dynamic Content using Mustache Templates?

How to Generate Dynamic Content using Mustache Templates?

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:

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 
                        https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <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 

  1. Methods and Properties of a Java class
  2. 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.

RELATED ARTICLES

Most Popular

Recent Comments