Creating Templates with Mustache
What is a mustache?
Mustache is nothing but a web template system. It is a “logic-less template engine” for creating dynamic content. Logic-less because it doesn’t support conditional statements like “if-else” and iterative statements like “for loops”. So for handling these types of conditions, we need to manipulate the data only, which can be done with the help of the lambdas and collections.
Why the name is mustache? Any special reason?
Yes, definitely the tag names are surrounded by {{}} that resembles a mustache, hence this template system is named as the mustache. These tags are backed by data.
The Mustache is using the Spring Web MVC Architecture. MVC is Model View Controller. The MVC is responsible for encapsulating the data in form of Java POJOs that will be further created into loosely coupled and flexible Web Applications.
Flow Diagram of Spring Boot Mustache
This flow diagram gives us an example of how a web application uses a Mustache.
Let’s start to create a simple HTML template of displaying articles on different topics with the help of mustache in few simple steps:
- Enabling Maven Dependency
We will be creating a Spring Boot Application, so we need Maven Dependency to enable Mustache in our application.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mustache</artifactId>
</dependency>
Apart from Mustache dependency, we need one more dependency “starter-web” for creating a web application.
2. Create An API
The Controller plays an important role here. It connects the data which is coming from the service to the HTML template.
package com.example.MustacheIntro.Controller;
import com.example.MustacheIntro.DTO.ArticleDTO;
import com.example.MustacheIntro.Service.ArticleDisplayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Controller
public class ArticleAPI {
@Autowired
ArticleDisplayService articleDisplayService;
@GetMapping("/article")
public ModelAndView displayArticle(Model model) {
List<ArticleDTO> articles = IntStream.range(0, 5)
.mapToObj(i -> articleDisplayService.generateArticle(i))
.collect(Collectors.toList());
model.addAttribute("articles",articles);
return new ModelAndView("article");
}
}
Here, the Model is a java object that carries the data, but by default it is null. There is a list of ArticleDTO that collects the description of different articles. The ArticleDTO comprises the id, name, and content of the Article.
package com.example.MustacheIntro.DTO;
public class ArticleDTO {
String articleId;
String name;
String content;
public ArticleDTO(String articleId, String name, String content) {
this.articleId = articleId;
this.name = name;
this.content = content;
}
public ArticleDTO(String articleId, String name) {
this.articleId = articleId;
this.name = name;
}
public void setArticleId(String articleId) {
this.articleId = articleId;
}
public void setName(String name) {
this.name = name;
}
public void setContent(String content) {
this.content = content;
}
public String getArticleId() {
return articleId;
}
public String getName() {
return name;
}
public String getContent() {
return content;
}
}
In the controller, the Intstream.range(int startIndex, int endIndex) will run a loop from 0 to 5 and fetch the data from the ArticleDisplayService.
package com.example.MustacheIntro.Service;
import com.example.MustacheIntro.DTO.ArticleDTO;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ArticleDisplayService {
public ArticleDTO generateArticle(int id) {
List<String> name = new ArrayList<>();
name.add("Introduction to Java8");
name.add("Introduction to Angular8");
List<String> content = new ArrayList<>(); content.add("JAVA 8 is a major feature release of JAVA programming language development. Its initial version was released on 18 March 2014. With the Java 8 release, Java provided supports for functional programming, new JavaScript engine, new APIs for date time manipulation, new streaming API, etc."); content.add("Angular 8 is a popular, open-source, and TypeScript framework that is compiled into JavaScript. With the latest version of Angular known as Angular 8, dynamic web applications can be created. It has also introduced some extensive features as compared to the previous versions.");
ArticleDTO description = new ArticleDTO("ID00"+(id+1),name.get(id), content.get(id));
return description;
}
}
Here, I have added the static data from the Lists but it can be fetched from the database also. Now the ArticleAPI will collect all the data from ArticleDisplayService in the List of ArticleDTO.
List<ArticleDTO> articles;
Now, we have data in articles, we will pass this to Model through Model.addAttribute() method. Model.addAttribute() method has two parameters String var1, @Nullable Object var2. The value of both the parameters will be the same as-
model.addAttribute("articles",articles);
As you can see the return type of the method defined is ModelAndView. ModelAndView is a holder for both Model and View Class that helps the controller to return both model and view in a single return value.
return new ModelAndView("article");
The parameter of ModelAndView must be equal to the name of the HTML file from which the data is needed to be populated onto the web application. For the extension identification that which type of file is associated in the application.properties, we provide the details of the port also(Not necessary, by default it will run at 8080).
spring.mustache.suffix= .html
server.port=9099
In the resources folder, we can see that there are two subfolders, static and templates. The templates folder will have the HTML files where the dynamic content is needed to be displayed and the static folder will have the designing part, eg- .css files.
In the template folder, we have three files:
1. intro.html — The introduction file. This will have all the initial HTML tags and the head tags.
<html>
<head>
<link rel="stylesheet" href="style.css" />
<h1>*********************************************************************************</h1>
<h1>Intro to Mustache</h1>
<h1>*********************************************************************************</h1>
</head>
<body>
In this file, we can see that we have embedded the .css file as well which is being fetched from the static folder from the resources folder.
h1 {color: blue}
2. article.html- This file will be having the main data that is being fetched from the services and it will display on a web browser.
{{>intro}}
{{#articles}}
<h2>{{articleId}}</h2>
<h1>{{name}}</h1>
<p>{{content}}</p>
{{/articles}}
{{>footer}}
As discussed above, the name of the file must be in the parameter of the ModelAndView. So the main joint of HTML and Spring is through the article.html file only.
Here, we can see that {{> name_of_file}} this will embed the content of other files as well.
3. footer.html- This file is having the ending tags of the HTML file.
</body>
</html>
Now you have added all the files and set the configuration. If you have added a code similar to the above, then the service will start.
When you hit the http://localhost:9099/article API, the response will look something like this.
Now, sometimes if want to populate the data onto an HTML file, but if the data is not present example if the content of a particular article is not present or the value of the content is null, then to prevent our application from throwing an error, we will add one bean in the MustacheIntroApplication.java.
package com.example.MustacheIntro;
import com.samskivert.mustache.Mustache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mustache.MustacheEnvironmentCollector;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class MustacheIntroApplication {
public static void main(String[] args) {
SpringApplication.run(MustacheIntroApplication.class, args);
}
@Bean
public Mustache.Compiler mustacheCompiler(Mustache.TemplateLoader templateLoader, Environment environment) {
MustacheEnvironmentCollector collector = new MustacheEnvironmentCollector();
collector.setEnvironment(environment);
return Mustache.compiler()
.defaultValue("Some Default Value")
.withLoader(templateLoader)
.withCollector(collector);
}
}
The Mustache.Compiler will set one default value that we want to add to our application. If the default value is not added, it will give an exception. So, to prevent this exception we will add this bean.
If the bean is not added, and if no value is present. Here, I have created the data of the article but without content.
ArticleDTO description = new ArticleDTO("ID00"+(id+1),name.get(id));
return description;
Now, when we hit the http://localhost:9099/article API, this will give the below error.
If we add the bean with some default value, on hitting the same API, it will give the result as:
Now, in place of the content, it will display some default value that was assigned in the bean.
Conclusion:
We have successfully created an HTML template with the help of Mustache in Spring Boot.