Features
It uses the open-source DayPilot Lite Calendar component to display a drag and drop weekly calendar.
You can download a Spring Boot application with JavaScript/HTML5 frontend.
It includes a lightweight REST API backend for accessing the database storage (implemented as a RESTful Spring web service).
The API is built using Spring Boot 3.0.6.
See also a related tutorial that shows how to create a monthly calendar in Spring Boot.
Code License
Apache License 2.0
HTML5 Event Calendar Initialization
In this step, we will start with the fronted that will be created as a simple HTML5 page with embedded JavaScript.
In order to display the JavaScript event calendar it's necessary to include the following code in the HTML page (src/resources/static/index.html
):
<!-- DayPilot Pro library-->
<script src="daypilot-all.min.js"></script>
<!-- placeholder, this is where the calendar appears -->
<div id="dp"></div>
<!-- basic calendar config -->
<script>
const dp = new DayPilot.Calendar("dp");
dp.init();
</script>
Required steps:
First, you need to include the DayPilot library in your project using a
script
tag.To create a Calendar component using DayPilot, you need to add a placeholder element to your HTML document where the calendar will appear. In the code, this element has the id
dp
and is represented by adiv
element.Then, you can create the calendar component by instantiating the
DayPilot.Calendar
object and passing the id of the placeholder element to the constructor. This will create an empty calendar on the page, using the default configuration (day view, default CSS theme).Finally, you need to call the
init
method on theDayPilot.Calendar
object to initialize the component and display it on the page.
Styling: The default CSS classes are embedded and there is no need to include additional CSS style files. You can create a custom CSS theme using the online CSS theme designer and apply it using theme
property.
Switching the Event Calendar to Week View
In order to switch the event calendar to a week view we simply use the viewType property. The calendar properties (and events handlers) can be specified using the options object that comes as the second argument of the DayPilot.Calendar
constructor.
<script>
const dp = new DayPilot.Calendar("dp", {
viewType: "Week"
});
dp.init();
</script>
Changing the Visible Date
By default, the event calendar displays the current week. You can set the initial date using using the startDate property:
<script>
const dp = new DayPilot.Calendar("dp", {
viewType: "Week",
startDate: "2021-06-21"
});
dp.init();
</script>
To change the visible week later, you can use the update() method and change the startDate
property as needed:
dp.update({
startDate: dp.startDate.addDays(7)
});
This example displays the following week in the calendar.
Changing the Calendar Locale
The week start is calculated using the current locale. By default, the locale is set to "en-us"
but you can change it as needed:
<script>
const dp = new DayPilot.Calendar("dp", {
viewType: "Week",
startDate: "2021-06-21",
locale: "de-de",
});
dp.init();
</script>
You can see that the locale affects the date format in the header (d.M.yyyy
vs M/d/yyyy
) and also the first day of week (Monday vs Sunday).
Changing the Date Format used in the Calendar Header
Every locale includes a default date format string which is used for the column headers. For the German locale it is "d.M.yyyy"
. You can also use a custom date format (headerDateFormat
property) to customize the header date format:
<script>
const dp = new DayPilot.Calendar("dp", {
viewType: "Week",
startDate: "2023-06-21",
locale: "de-de",
headerDateFormat: "d MMMM yyyy"
});
dp.init();
</script>
Online Calendar Configurator App
You can use the UI Builder online application to explore some of the most common configuration properties. The application lets you preview the changes using a live calendar component instances and download a project that includes a calendar setup with the selected configuration. It supports JavaScript, TypeScript, Angular, React and Vue projects.
Switching a Date using a Date Picker
DayPilot includes a date picker component (DayPilot.Navigator) that lets users choose a date using a mini calendar.
We will add a Navigator placeholder to the HTML:
<div id="nav"></div>
And initialize it:
<script>
const datePicker = new DayPilot.Navigator("nav", {
showMonths: 3,
skipMonths: 3,
selectMode: "week"
});
nav.init();
</script>
Now we need to detect changes of the selected week and update the event calendar component accordingly:
<script>
const nav = new DayPilot.Navigator("nav", {
showMonths: 3,
skipMonths: 3,
selectMode: "week",
onTimeRangeSelected: (args) => {
dp.startDate = args.day;
dp.update();
}
});
nav.init();
</script>
Loading Calendar Data in Spring Boot
In order to load the event data from a database, we will call events.load() method:
dp.events.load("/api/events");
The event calendar component automatically adds the visible range to this URL (using start
and end
query string parameters) and makes an HTTP request to get the event data. The sample JSON response looks like this:
[
{"id":3,"text":"Meeting","start":"2023-06-14T10:00:00","end":"2023-06-14T13:00:00","color":null},
{"id":4,"text":"Errands","start":"2023-06-16T10:00:00","end":"2023-06-16T13:00:00","color":"#f1c232"}
]
The server-side implementation of the JSON endpoint can be found in MainController.java
file:
package org.daypilot.demo.html5eventcalendarspring.controller;
//...
@RestController
public class MainController {
@Autowired
EventRepository er;
@GetMapping("/api/events")
@JsonSerialize(using = LocalDateTimeSerializer.class)
Iterable<Event> events(@RequestParam("start") @DateTimeFormat(iso = ISO.DATE_TIME) LocalDateTime start, @RequestParam("end") @DateTimeFormat(iso = ISO.DATE_TIME) LocalDateTime end) {
return er.findBetween(start, end);
}
// ...
}
The Spring controller is very simple and uses an EventRepository
interface (that inherits from CrudRepository<Event, Long>
) to load the event data.
package org.daypilot.demo.html5eventcalendarspring.repository;
import java.time.LocalDateTime;
import java.util.List;
import org.daypilot.demo.html5eventcalendarspring.domain.Event;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
public interface EventRepository extends CrudRepository<Event, Long> {
@Query("from Event e where not(e.end < :from or e.start > :to)")
public List<Event> findBetween(@Param("from") @DateTimeFormat(iso=ISO.DATE_TIME) LocalDateTime start, @Param("to") @DateTimeFormat(iso=ISO.DATE_TIME) LocalDateTime end);
}
The Event
model class defines the event record structure:
package org.daypilot.demo.html5eventcalendarspring.domain;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
public class Event {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String text;
@Column(name = "event_start")
LocalDateTime start;
@Column(name = "event_end")
LocalDateTime end;
String color;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public LocalDateTime getStart() {
return start;
}
public void setStart(LocalDateTime start) {
this.start = start;
}
public LocalDateTime getEnd() {
return end;
}
public void setEnd(LocalDateTime end) {
this.end = end;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
This Spring Boot project uses H2 in-memory database that is automatically initialized on startup (application.properties
):
spring.datasource.url=jdbc:h2:mem:mydb
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.defer-datasource-initialization=true
spring.jackson.serialization.write_dates_as_timestamps=false
server.port=${port:8081}
Saving Updated Calendar Events to the Database in Spring Boot
Whenever the user moves an event using drag and drop, the Calendar fires an onEventMove event handler. We can use this event to notify the server using an HTTP API call:
const dp = new DayPilot.Calendar("dp", {
onEventMove: async (args) => {
const params = {
id: args.e.id(),
start: args.newStart,
end: args.newEnd
};
const {data} = await DayPilot.Http.post("/api/events/move", params);
console.log("Event moved");
},
// ...
});
The onEventMove
property is set to an async function that receives an args
object containing information about the moved event, including its id
, newStart
, and newEnd
. We then make an HTTP POST request to /api/events/move
with these parameters, and log a message to the console when the move is complete.
On the server side, this API call is handled by the moveEvent
method of the MainController
class:
package org.daypilot.demo.html5eventcalendarspring.controller;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.daypilot.demo.html5eventcalendarspring.domain.Event;
import org.daypilot.demo.html5eventcalendarspring.repository.EventRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.web.bind.annotation.*;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
@RestController
public class MainController {
@Autowired
EventRepository er;
@PostMapping("/api/events/move")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@Transactional
Event moveEvent(@RequestBody EventMoveParams params) {
Event e = er.findById(params.id).get();
e.setStart(params.start);
e.setEnd(params.end);
er.save(e);
return e;
}
public static class EventMoveParams {
public Long id;
public LocalDateTime start;
public LocalDateTime end;
public Long resource;
}
// ...
}
It updates the start
and end
properties of the Event
instance and saves the changes to the database.
Maven Dependencies (pom.xml)
Here is the Maven pom.xml
file that loads the required dependencies (Spring Boot framework and H2 database):
pom.xml
<?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>
<groupId>org.daypilot.demo</groupId>
<artifactId>html5-event-calendar-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>19</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>