Spring Boot + Bootstrap + Thymeleaf Time Picker

July 06, 2020 No comments Spring Boot Bootstrap Thymeleaf Slider

Spring Boot + Bootstrap + Thymeleaf Time Picker

1. Introduction

In this article, we are going to present a custom Thymeleaf Time Picker embedded in a Spring Boot application. We use a custom Time Picker component to enter time on the Thymeleaf form. The provided value will be submitted to the backend and presented on another page.

For more information about Thymeleaf go and check below links:
Thymeleaf related articles organized in several categories
How to configure Thymeleaf with Spring Boot
Using Forms in Thymeleaf

2. Dependencies

2.1. Maven dependencies

Project was built using three common Maven dependencies:

2.2. Front-End libraries

Presentation layer use several libraries and frontend frameworks:

  • bootstrap - framework for building responsive websites,
  • moment - javascript library for time/date management,
  • bootstrap-datetimepicker - custom time picker component,
  • jquery - required by bootstrap.

Maven pom.xml file have 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-timepicker</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

The sample application presented in this article will use Thymeleaf form to submit information about Event like Title, Description and Time. The time of the event will be selected using a custom Time Picker component.

We have one controller - IndexController class that handles GET request to the root context and POST request with provided form data.

The IndexController 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.Event;

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

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

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

The model layer contains a single object Event with the following structure:

package com.frontbackend.thymeleaf.bootstrap.model;

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

@Getter
@Setter
@NoArgsConstructor
public class Event {

    private String title;
    private String description;
    private String time;

}

The Event object is our base object used in the Thymeleaf form (command object).

Spring Boot main class that starts the server has the following structure:

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. Template

We created two Thymeleaf templates:

  • index.html - contains Thymeleaf form with Time Picker component,
  • saved.html - will present submitted data from the previous view.

The index.html 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 Time Picker</title>

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

    <link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.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 Time Picker</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="${event}">

                <div class="form-group">
                    <label for="title">Title:</label>
                    <input type="text" id="title" placeholder="Title" class="form-control" th:field="*{title}"/>
                </div>

                <div class="form-group">
                    <label for="description">Description:</label>
                    <textarea type="text" rows="2" id="description" placeholder="Description" class="form-control"
                              th:field="*{description}"></textarea>
                </div>

                <div class="form-group">
                    <label for="time">Time:</label>
                    <div class="input-group date" id="event_time">
                        <input type="text" id="time" value="11:31:00" placeholder="Time" class="form-control" th:field="*{time}"/>
                        <div class="input-group-addon input-group-append">
                            <div class="input-group-text">
                                <i class="fa fa-clock-o"></i>
                            </div>
                        </div>
                    </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/moment/moment.min.js}"></script>
<script th:src="@{assets/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js}"></script>

<script>
    $('#event_time').datetimepicker({
        "allowInputToggle": true,
        "showClose": true,
        "showClear": true,
        "showTodayButton": true,
        "format": "HH:mm:ss",
    });
</script>

</body>
</html>

To create Time Picker we use bootstrap-datetimepicker library available under GitHub Repository - https://github.com/monim67/bootstrap-datetimepicker/. With this library we can also create complex time/date picker components.

The saved.html view presents submitted data:

<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>Spring Boot Thymeleaf Application - Bootstrap Time Picker</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 Time Picker</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 mb-4">Event submitted</h2>

    <p><label>Title: </label> <strong th:text="${event.title}"></strong></p>
    <p><label>Description: </label> <strong th:text="${event.description}"></strong></p>
    <p><label>Time: </label> <strong th:text="${event.time}"></strong></p>

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

</body>
</html>

5. The output

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

Thymeleaf bootstrap time picker

6. Conclusion

In this article, we presented how to build a form with Thymeleaf Time Picker component in a Spring Boot application. We used component that can be easily operated on mobile devices.

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

{{ message }}

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