7 minutes read

When we want to create a well-structured web page, we use HTML along with some styling to make it even better-looking. One problem here is that sometimes static HTML does not help much, especially when we need to display changing content on our website.

In this topic, you will learn how to create dynamic web pages using FreeMarker in Spring Boot applications.

FreeMarker template engine

FreeMarker is a free general purpose template engine from the Apache Software Foundation. It is a Java library, which means it is interoperable with Kotlin, that generates text output based on templates. A template is a file where you give instructions on how to represent data using a specific syntax. At runtime the template engine will step in and generate the desired output from your template, populating it with actual data just as you specified: your template, your rules!

This library is designed to be practical for the generation of HTML web pages, especially in applications following the MVC (Model View Controller) pattern, as it was originally created for that purpose.

To use FreeMarker, you need to import it. For Gradle based projects, you should add the following dependency to your build.gradle orbuild.gradle.kts file.

Groovy DSL
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
Kotlin DSL
implementation("org.springframework.boot:spring-boot-starter-freemarker")

As for Maven-based ones, add the following dependency to your pom.xml file.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

FreeMarker HTML template

FreeMarker provides a simple specialized language called FTL (FreeMarker Template Language) that is used to create templates. You can think of templates as programs written in FTL, and consisting of the following sections:

  • text: a static text that will be printed to the output as it is (in our case, it's normal HTML syntax);

  • interpolations: these are the sections that will be replaced with actual values in the output, and are delimited by ${ and };

  • FTL tags: these are a bit similar to HTML tags, except that they are instructions to FreeMarker, and they will not be printed in the output;

  • comments: these are delimited by <#-- and -->, and will be ignored in the output.

A simple HTML template to greet a user would look like this:

<html lang="en">
<head>
    <title>Welcome</title>
</head>
<body>
    <p>Welcome to our platform, ${name}</p>
</body>
</html>

Here, we used interpolation to display the user's name. Whenever the welcome page is requested, FreeMarker will generate a plain HTML web page from this template by replacing the name with the up-to-date value retrieved from a data-model. Here is an example of the generated result:

<html lang="en">
<head>
    <title>Welcome</title>
</head>
<body>
    <p>Welcome to our platform, John</p>
</body>
</html>

For Spring Boot applications, HTML template files should be placed under the src/main/resources/templates directory. Also, the file name should have the .ftlh extension.

Binding data

Templates do not calculate or contain data on their own. Instead, we need to bind it to them from a controller using a model. First, to create a controller, Spring provides the @Controller annotation. It is used to indicate that a Java/Kotlin class is a component in which we define handler methods for different requests (GET, POST, etc.). We usually use it when handler methods are returning views, like web pages, in response to user requests.

A model serves as a container for the application data. To expose it to the view template, we should use the Model interface by passing a model object as a parameter in the handler method. After that, we can add attributes to that model through a set of methods provided by the interface: addAttribute(String name, Object value) in Java or addAttribute(name: String, value: Any?) in Kotlin for adding a named attribute, addAllAttributes(Collection<?> values) in Java or addAllAttributes(values: MutableCollection<*>) in Kotlin to add a collection of attributes, and some other useful methods.

Now that FreeMarker has ready access to the model attributes and can render templates smoothly, let's see how all these things work together in a simple application example.

An example

Let's suppose that we want to create a simple website for a pizzeria. When a visitor enters, we should show them a menu with all the available pizzas and their prices. To make that possible, let's create a basic Spring Boot project, give it a name of your choice and remember to import FreeMarker as shown before.

With everything in place, let's create a class for our pizza object:

Java
public class Pizza {

    private String name;
    private double price;

    // constructor 

    // getters and setters (must be implemented!)
}
Kotlin
data class Pizza(var name: String, var price: Double)

After that, we need to create a controller that handles requests for our website:

Java
@Controller
public class PizzeriaController {
    private List<Pizza> pizzaList = List.of(
            new Pizza("Margherita", 5.0),
            new Pizza("Napoletana", 6.0),
            new Pizza("Calzone", 7.5)
    );

    @GetMapping("/home")
    public String home(Model model) {
        model.addAttribute("pizzas", pizzaList);
        return "menu";
    }
}
Kotlin
@Controller
class PizzeriaController {
    private val pizzaList: List<Pizza> = listOf(
        Pizza("Margherita", 5.0),
        Pizza("Napoletana", 6.0),
        Pizza("Calzone", 7.5)
    )

    @GetMapping("/home")
    fun home(model: Model): String {
        model.addAttribute("pizzas", pizzaList)
        return "menu"
    }
}

In the above controller, we created a list of the available pizzas and used the @GetMapping annotation to indicate that the home method will handle GET requests with the /home path. We passed a model to this method, then we added pizzaList as an attribute named pizzas to that model. This method will return the menu view, which is an HTML template.

Note that the return type for the home handler method is String and that we refer to the returned template file with just its name menu, no extension.

Last but not least, we have to create the template file with the name specified before menu.ftlh:

<html lang="en">
<head>
    <title>Menu</title>
</head>
<body>
<h1>Our delicious menu</h1>
<table>
    <tr>
        <th>Pizza</th>
        <th>Price (USD)</th>
    </tr>
    <#list pizzas as pizza>
        <tr>
            <td>${pizza.name}</td>
            <td>${pizza.price}</td>
        </tr>
    </#list>
</table>
</body>
</html>

To display the menu, we used an HTML <table> tag with a row for the table header and a #list FTL tag. The latter is a directive that tells FreeMarker to loop over the pizzas list retrieved from our model. For that, it uses a new pizza variable to refer to the current element and render the body (the code between the start and the end tag) of this directive for each element on the list.

Since our model contains a list of Pizza objects, we used the ${pizza.name} interpolation to insert the value of the name property of the current pizza element into the <td> tag. The same goes for ${pizza.price}.

The final result

Now that we implemented everything we need, let's run our application and see the results. To get to the pizzeria homepage, open your favorite browser and go to http://localhost:8080/home.

With some CSS styling, the generated web page would look something like this:

spring mvc freemarker table

In our example, the menu data are most likely to change, prices may go up or down, or we may add new pizzas, yet we don't have to accordingly modify our HTML anymore: the changes will be reflected as soon as a user requests the page.

Conclusion

In this topic, we have learned how to create dynamic web pages using FreeMarker. This tool enabled us to use templates in our application. With the help of its template language, we were able to represent data prepared in the back-end to the view layer through FTL directives and interpolations. FreeMarker's capabilities go far beyond what we have already seen: you can refer to the documentation for more details on its use.

42 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo