Spring Boot + Bootstrap + Thymeleaf Select Option

July 06, 2020 1 Comment Thymeleaf Select Spring Boot

Spring Boot + Bootstrap + Thymeleaf Select Option

1. Introduction

In this article, we are going to present Thymeleaf Single/Multi-Select components embedded in a Spring Boot application. For the base layout framework, we choose Bootstrap that allows creating responsive websites easily.

More information about how to configure Thymeleaf for Spring Boot and how to start working with forms, check below links:
Spring Boot with Thymeleaf
Thymeleaf Forms

2. Maven dependencies

The example application use several libraries:

The Maven pom.xml file with all required dependencies looks like the following:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>thymeleaf-bootstrap-select-option</artifactId>

    <properties>
        <bootstrap.version>4.0.0-2</bootstrap.version>
        <webjars-locator.version>0.30</webjars-locator.version>
        <lombok.version>1.18.2</lombok.version>
    </properties>

    <!-- Inherit defaults from Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
    </parent>

    <!-- Add typical dependencies for a web application -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>${bootstrap.version}</version>
        </dependency>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator</artifactId>
            <version>${webjars-locator.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!-- Package as an executable jar -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3. Model, Controller and Application class

Spring web controller was defined to handle all GET and POST requests to the main application path. The base model object Order contains an enum field Pizza and an array of Dip objects. The Order object will be used as the main form of the command object.

The Order class used in this example application have the following structure:

package com.frontbackend.thymeleaf.bootstrap.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Setter
@Getter
@NoArgsConstructor
public class Order {

    private Pizza pizza;
    private Dip[] dips;

}

Pizza enum object looks like the following:

package com.frontbackend.thymeleaf.bootstrap.model;

public enum Pizza {

    MARGHERITA,
    PEPPERONI,
    CAPRICCIOSA,
    MUSHROOM,
    MARINARA,
    SEAFOOD,
    HAWAIIAN,
    VEGETARIANA,
    MEXICAN

}

Dip enum object looks like the following:

package com.frontbackend.thymeleaf.bootstrap.model;

public enum Dip {

    KETCHUP,
    MAYONNAISE,
    GARLIC
}

Web controller class have the following structure:

package com.frontbackend.thymeleaf.bootstrap.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.frontbackend.thymeleaf.bootstrap.model.Order;

@Controller
@RequestMapping({ "/", "/index" })
public class IndexController {

    @GetMapping
    public String main(Model model) {
        model.addAttribute("order", new Order());
        return "index";
    }

    @PostMapping
    public String save(Order order, Model model) {
        model.addAttribute("order", order);
        return "saved";
    }
}

The main method in Application class starts the Spring Boot application server:

package com.frontbackend.thymeleaf.bootstrap;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4. Templates

We defined two Thymeleaf templates in the /resources/templates directory:

  • index.html - to present the main form with single and multi-select components,
  • saved.html - where we are going to present selected from the previous page options.

The index.html file will have the following structure:

<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Spring Boot Thymeleaf Application - Bootstrap Select Option</title>

    <link th:rel="stylesheet" th:href="@{assets/bootstrap-select-1.13.9/dist/css/bootstrap-select.css}"/>
    <link th:rel="stylesheet" th:href="@{webjars/bootstrap/4.0.0-2/css/bootstrap.min.css} "/>

</head>
<body>

<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
    <div class="container">
        <a class="navbar-brand" href="/">Thymeleaf - Bootstrap Select Option</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="#">Home
                        <span class="sr-only">(current)</span>
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Services</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Contact</a>
                </li>
            </ul>
        </div>
    </div>
</nav>

<div class="container">

    <div class="row">
        <div class="col-md-4 mt-5">
            <form method="post" th:object="${order}">

                <div class="form-group">
                    <label for="pizza">Choose pizza</label>
                    <select class="form-control selectpicker" th:field="*{pizza}" id="pizza">
                        <option value="">Nothing selected</option>
                        <option th:each="pizza : ${T(com.frontbackend.thymeleaf.bootstrap.model.Pizza).values()}"
                                th:value="${pizza}"
                                th:text="${pizza}">pizza
                        </option>

                    </select>
                </div>

                <div class="form-group">
                    <label for="dips">Select dips</label>
                    <select class="form-control selectpicker" th:field="*{dips}" id="dips" multiple>

                        <option th:each="dip : ${T(com.frontbackend.thymeleaf.bootstrap.model.Dip).values()}"
                                th:value="${dip}"
                                th:text="${dip}">dip
                        </option>

                    </select>
                </div>

                <button class="btn btn-primary" type="submit">Submit form</button>
            </form>
        </div>
    </div>

</div>


<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script th:src="@{/webjars/popper.js/umd/popper.min.js}"></script>
<script th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
<script th:src="@{assets/bootstrap-select-1.13.9/dist/js/bootstrap-select.js}"></script>

</body>
</html>

We used special T expression to retrieve all enum values:

  • ${T(com.frontbackend.thymeleaf.bootstrap.model.Pizza).values()}",
  • ${T(com.frontbackend.thymeleaf.bootstrap.model.Dip).values()}".

To brings select elements into the 21st century we used bootstrap-select library.

The save.html file will have the following structure:

<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Spring Boot Thymeleaf Application - Bootstrap Select Option</title>

    <link th:rel="stylesheet" th:href="@{webjars/bootstrap/4.0.0-2/css/bootstrap.min.css} "/>
</head>
<body>

<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top">
    <div class="container">
        <a class="navbar-brand" href="/">Thymeleaf - Bootstrap Select Option</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="#">Home
                        <span class="sr-only">(current)</span>
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Services</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Contact</a>
                </li>
            </ul>
        </div>
    </div>
</nav>

<div class="container">
    <div class="mt-5">Pizza: <strong th:text="${order.pizza}"></strong></div>
    <div>Dips: <strong th:text="${#strings.listJoin(order.dips, ',')}"></strong></div>

    <a th:href="@{/}" class="btn btn-primary mt-5">Go back</a>
</div>

</body>
</html>

5. The output

Started application is available under http://locahost:8080 URL address, and presents the following functionality:

Thymeleaf bootstrap select options

6. Conclusion

In this article, we presented how to use Thymeleaf Single and Multi-Select components in a Spring Boot application using the Thymeleaf template engine.

The code used in this article is available under our GitHub repository.

{{ message }}

{{ 'Comments are closed.' | trans }}