Spring Boot + Bootstrap + Thymeleaf Date Range Picker

July 06, 2020 No comments Spring Boot Bootstrap Thymeleaf Date Range Picker

Spring Boot + Bootstrap + Thymeleaf Date Range Picker

1. Introduction

In this article, we are going to present Thymeleaf Date Range Picker component. Date pickers with range (date from and to) are common components used to filter elements with a time stamp. Here we will show a sample application that uses date range picker and splits selected value into two fields so it will be easy to process it on the backend.

You can find more information about Thymeleaf and Forms in the following links:
Thymeleaf Tutorial
Working with Forms in Thymeleaf

2. Dependencies

2.1. Maven dependencies

To show how Date Range Picker works, we used a sample Spring Boot application created as a Maven project wiith three common dependencies:

2.2. Front-End libraries
  • bootstrap - a frontend framework for creating responsive websites.
  • daterangepicker - a JavaScript library for creating date range picker components.

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-date-range-picker</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>
        <font-awesome.version>5.11.2</font-awesome.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.webjars</groupId>
            <artifactId>font-awesome</artifactId>
            <version>${font-awesome.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

Selected value in the Date Range Picker component will be split into two fields: dateFrom and dateTo. In the model layer we have DateRange object with those two fields:

package com.frontbackend.thymeleaf.bootstrap.model;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

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

@Setter
@Getter
@NoArgsConstructor
public class DateRange {

    @DateTimeFormat(pattern = "dd/MM/yyyy")
    private Date dateFrom;

    @DateTimeFormat(pattern = "dd/MM/yyyy")
    private Date dateTo;

}

POST and GET requests are handled by IndexController class which is a Spring Web Controller.

package com.frontbackend.thymeleaf.bootstrap.controller;

import java.util.Date;

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.DateRange;

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

    @GetMapping
    public String main(Model model) {
        DateRange dateRange = new DateRange();
        dateRange.setDateFrom(new Date());
        dateRange.setDateTo(new Date());
        model.addAttribute("dateRange", dateRange);
        return "index";
    }

    @PostMapping
    public String save(DateRange dateRange, Model model) {
        model.addAttribute("dateRange", dateRange);
        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 views:

  • index.html - here user can select the date from and date to in the date range picker component,
  • saved.html - this view is used to present the submitted values.

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 Date Range Picker</title>

    <link th:rel="stylesheet" th:href="@{assets/daterangepicker/daterangepicker.css}"/>
    <link th:rel="stylesheet" th:href="@{webjars/bootstrap/4.0.0-2/css/bootstrap.min.css} "/>
    <link th:rel="stylesheet" th:href="@{webjars/font-awesome/5.11.2/css/all.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 Date Range 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">
    <form method="post" th:object="${dateRange}">
        <div class="form-group row">
            <div class="col-md-4 mt-5">
                <label for="dateRange">Date of birth</label>
                <div class="input-group">
                    <input type="text" class="form-control" id="dateRange" autocomplete="off"/>
                    <div class="input-group-append">
                        <span class="input-group-text"><i class="far fa-calendar-alt"></i></span>
                    </div>
                </div>
            </div>

            <input type="hidden" name="dateFrom" th:field="*{dateFrom}"/>
            <input type="hidden" name="dateTo" th:field="*{dateTo}"/>
        </div>

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

</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/daterangepicker/daterangepicker.js}"></script>

<script>
    var $dateRange = $('#dateRange');

    $dateRange.daterangepicker();
    $dateRange.on('apply.daterangepicker', function (ev, picker) {
        $('input[name="dateFrom"]').val(picker.startDate.format('DD/MM/YYYY'));
        $('input[name="dateTo"]').val(picker.endDate.format('DD/MM/YYYY'));
    });
</script>

</body>
</html>

For creating responsive Date Range Picker component we used external JavaScript library: https://github.com/dangrossman/daterangepicker.

To initialize daterangepicker library, use below code:

var $dateRange = $('#dateRange');

    $dateRange.daterangepicker();
    $dateRange.on('apply.daterangepicker', function (ev, picker) {
        $('input[name="dateFrom"]').val(picker.startDate.format('DD/MM/YYYY'));
        $('input[name="dateTo"]').val(picker.endDate.format('DD/MM/YYYY'));
    });

The saved.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 Date Range 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 Date Range 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-sm-12 mt-3">
            <div class="form-group">
                <span>Date from:</span> <strong th:text="${#dates.format(dateRange.dateFrom, 'yyyy-MM-dd')}"></strong>
            </div>

            <div class="form-group">
                <span>Date to:</span> <strong th:text="${#dates.format(dateRange.dateTo, 'yyyy-MM-dd')}"></strong>
            </div>
        </div>
    </div>

    <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 date range picker

6. Conclusion

In this article, we presented Thymeleaf Date Range Picker component. We used a JavaScript library that is compatible with Bootstrap and allows us to create responsive components.

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

{{ message }}

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