Spring Boot + Bootstrap + Thymeleaf Switch

July 06, 2020 No comments Spring Boot Bootstrap Thymeleaf Switch

Spring Boot + Bootstrap + Thymeleaf Switch

1. Introduction

In this article, we will present the Thymeleaf Switch component that allows selecting true/false values in Thymeleaf forms.

Looking for more useful informations about Thymeleaf? check the following articles:
Thymeleaf Tutorials
Forms in Thymeleaf

2. Dependencies

2.1. Maven dependencies

To present how the Switch component works with Thymeleaf we used a simple Spring Boot application created as a Maven project where we used the following dependencies:

2.2. Front-End libraries
  • bootstrap - a frontend framework for creating responsive websites,
  • bootstrap-switch - a JavaScript library for creating switch components.

Project Maven pom.xml file has the following structure:

<?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-switch</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 Main Application class

The model layer contains a single class that will store known languages as boolean fields:

package com.frontbackend.thymeleaf.bootstrap.model;

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

@Setter
@Getter
@NoArgsConstructor
public class KnownLanguages {

    private boolean java;
    private boolean css;
    private boolean angular;
    private boolean react;
    private boolean python;
    private boolean go;

}

GET and POST requests on the root context are handled by a simple Spring Web Controller with 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.KnownLanguages;

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

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

    @PostMapping
    public String save(KnownLanguages skills, Model model) {
        model.addAttribute("languages", skills);
        return "saved";
    }
}

The Application is the Java class with the main method that 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

The presentation layer contains two Thymeleaf templates:

  • index.html - a view that presents the switch component with options to select,
  • saved.html - the result view that presents selected known languages.

The index.html template has 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 Switch</title>

    <link th:rel="stylesheet" th:href="@{assets/bootstrap-switch/bootstrap-switch.min.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 Switch</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">
            <h2 class="mt-5">Known languages</h2>

            <form method="post" th:object="${languages}">
                <div class="ml-2">
                    <div class="form-group mt-3">
                        <span class="ml-2">JAVA <input type="checkbox" th:field="*{java}" data-toggle="switch"/></span>
                    </div>

                    <div class="form-group mt-3">
                        <span class="ml-2">CSS <input type="checkbox" th:field="*{css}" data-toggle="switch"/></span>
                    </div>

                    <div class="form-group mt-3">
                        <span class="ml-2">Angular <input type="checkbox" th:field="*{angular}" data-toggle="switch"/></span>
                    </div>

                    <div class="form-group mt-3">
                        <span class="ml-2">Python <input type="checkbox" th:field="*{python}" data-toggle="switch"/></span>
                    </div>

                    <div class="form-group mt-3">
                        <span class="ml-2">React <input type="checkbox" th:field="*{react}" data-toggle="switch"/></span>
                    </div>

                    <div class="form-group mt-3">
                        <span class="ml-2">Go <input type="checkbox" th:field="*{go}" data-toggle="switch"/></span>
                    </div>
                </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-switch/bootstrap-switch.min.js}"></script>

<script>
    $('[data-toggle="switch"]').bootstrapSwitch();
</script>

</body>
</html>

The custom switch components were built using external JavaScript library bootstrap-switch: https://github.com/Bttstrp/bootstrap-switch.

To initialize the library we used the following JavaScript code:

$('[data-toggle="switch"]').bootstrapSwitch();

The saved.html file has 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 Switch</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 Switch</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">
    <h2 class="mt-5">Known languages</h2>

    <p>JAVA: <strong class="badge" th:text="${languages.java}"
                     th:classappend="${languages.java ? 'badge-success' : 'badge-secondary'}"></strong>
    </p>

    <p>CSS: <strong class="badge" th:text="${languages.css}"
                    th:classappend="${languages.css ? 'badge-success' : 'badge-secondary'}"></strong>
    </p>

    <p>Angular: <strong class="badge" th:text="${languages.angular}"
                        th:classappend="${languages.angular ? 'badge-success' : 'badge-secondary'}"></strong>
    </p>

    <p>Python: <strong class="badge" th:text="${languages.python}"
                       th:classappend="${languages.python ? 'badge-success' : 'badge-secondary'}"></strong>
    </p>

    <p>React: <strong class="badge" th:text="${languages.react}"
                      th:classappend="${languages.react ? 'badge-success' : 'badge-secondary'}"></strong>
    </p>

    <p>Go: <strong class="badge" th:text="${languages.go}"
                   th:classappend="${languages.go ? 'badge-success' : 'badge-secondary'}"></strong>
    </p>

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

</body>
</html>

5. The output

The running application is available under http://locahost:8080 URL and presents the following functionality:

Thymeleaf bootstrap switch

6. Conclusion

In this article, we presented the Thymeleaf Switch component. Switches are used to let the user select one or more options for a limited number of choices. This article covers how we can use that component with the Thymeleaf template engine.

As usual, the code used in this article is available under our GitHub repository.

{{ message }}

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