Features
Java 22
Spring Boot 3.3.1
Maven project configuration (
pom.xml
)Data persistence using Spring Data/JPA (Hibernate in-memory database)
Separate frontend (HTML5/JavaScript) and backend (Spring web service)
Includes DayPilot Lite for JavaScript (open-source)
License
Apache License 2.0
Monthly Calendar Component Initialization in Spring Boot
This is the minimum code required to use the JavaScript monthly event calendar in an HTML5 page:
Include the DayPilot JavaScript scheduling library (
daypilot-all.min.js
).Add a placeholder element at the place where you want to display the monthly calendar.
Initialize the monthly component using a new DayPilot.Month class instance.
Our Spring Boot application only has a single HTML5 page (index.html
). You can find it in the src/main/resources/static
directory.
<!-- monthly calendar control placeholder -->
<div id="dp"></div>
<!-- DayPilot library -->
<script src="js/daypilot/daypilot-all.min.js"></script>
<!-- calendar initialization and config -->
<script>
const calendar = new DayPilot.Month("dp");
calendar.init();
</script>
Setting the Initial Month
By default, the calendar component displays the current month. You can change the initial date by adding a startDate property to the calendar config:
<script>
const calendar = new DayPilot.Month("dp", {
startDate: "2024-10-01"
});
calendar.init();
</script>
Instead of a static date specified using a string, you can use a DayPilot.Date class. It will let you calculate the date easily.
The following example displays the next month in the calendar component:
<script>
const calendar = new DayPilot.Month("dp", {
startDate: DayPilot.Date.today().addMonths(1)
});
calendar.init();
</script>
Loading and Displaying Events in the Monthly Calendar
We will load the event data using an HTTP call to a REST/JSON endpoint. The monthly calendar component supports a convenience method for loading event data from a specified URL:
calendar.events.load("/api/events");
The URL will be automatically extended with the start and end date of the current month view. The start and end dates will be added as query string parameters:
http://localhost:8081/api/events?start=2024-01-01T00:00:00&end=2024-02-05T00:00:00
The /api/events
URL is handled by a Spring controller on the server side (CalendarController.java
). It returns event data for the specified date range:
package org.daypilot.demo.html5eventcalendarspring.controller;
// ...
@RestController
public class CalendarController {
@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);
}
// ...
}
Changing the Current Month Displayed by the Calendar
You can change the month by setting a new startDate
value using the update() method:
calendar.update({
startDate: "2024-02-01"
});
To switch the calendar to the next month, you can use the current startDate
value (make sure that it’s a DayPilot.Date
class) and and add one month to it:
calendar.update({
startDate: calendar.startDate.addMonths(1)
});
After changing the date, it will be necessary to load the corresponding events using the events.load()
method:
const currentDate = calendar.startDate;
calendar.update({
startDate: currentDate.addMonths(1)
});
calendar.events.load("/api/events");
Event Deleting using a Context Menu
To let users delete calendar event, we will add a context menu.
You can specify the context menu the contextMenu
property of the DayPilot.Month
object. It's a DayPilot.Menu
object that specifies menu items
(including text and click event handler) and other properties.
HTML5 view
const calendar = new DayPilot.Month("dp", {
contextMenu: new DayPilot.Menu({
items: [
{
text: "Delete",
onClick: async (args) => {
const e = args.source;
const params = {
id: e.id()
};
const {data} = await DayPilot.Http.post('/api/events/delete', params);
calendar.events.remove(e);
}
},
// ...
]
})
});
By default, the context menu opens on right click. Since it's not obvious that this option is available, we will add a context menu hint to the event box using an event active area. The active area uses "ContextMenu"
as its action
- this opens the default context menu specified using DayPilot.Month.contextMenu
property.
const calendar = new DayPilot.Month("dp", {
onBeforeEventRender: args => {
const color = args.data.color || app.colors.gray;
args.data.backColor = color;
args.data.borderColor = "darker";
args.data.fontColor = "#ffffff";
args.data.areas = [
{
top: 6,
right: 6,
width: 18,
height: 18,
symbol: "../icons/daypilot.svg#minichevron-down-2",
action: "ContextMenu",
backColor: "#ffffff",
fontColor: "#666666",
style: "border: 1px solid #ccc; cursor:pointer; border-radius: 15px;"
}
];
},
contextMenu: new DayPilot.Menu({
// ...
}),
// ...
});
The "Delete" context menu item uses an HTTP call to delete the item from the monthly calendar. The Spring Boot controller that implements the REST API contains deleteEvent()
method that handles calls to /api/events/delete
URL.
package org.daypilot.demo.html5eventcalendarspring.controller;
// ...
@RestController
public class CalendarController {
@Autowired
EventRepository er;
@PostMapping("/api/events/delete")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@Transactional
EventDeleteResponse deleteEvent(@RequestBody EventDeleteParams params) {
er.delete(params.id);
return new EventDeleteResponse() {{
message = "Deleted";
}};
}
public static class EventDeleteParams {
public Long id;
}
public static class EventDeleteResponse {
public String message;
}
// ...
}
Changing Color of Calendar Events
Now we will extend our context menu with a list of custom event colors. Selecting the context menu item will update the event color in the database an update it in the monthly calendar in the browser.
const calendar = new DayPilot.Month("dp", {
contextMenu: new DayPilot.Menu({
items: [
{
text: "Delete",
onClick: async (args) => {
const e = args.source;
const params = {
id: e.id()
};
const {data} = await DayPilot.Http.post('/api/events/delete', params);
calendar.events.remove(e);
}
},
{
text: "-"
},
{
text: "Blue",
icon: "icon icon-blue",
onClick: (args) => {
app.updateColor(args.source, app.colors.blue);
}
},
{
text: "Green",
icon: "icon icon-green",
onClick: (args) => {
app.updateColor(args.source, app.colors.green);
}
},
{
text: "Yellow",
icon: "icon icon-yellow",
onClick: (args) => {
app.updateColor(args.source, app.colors.yellow);
}
},
{
text: "Red",
icon: "icon icon-red",
onClick: (args) => {
app.updateColor(args.source, app.colors.red);
}
}, {
text: "Auto",
onClick: (args) => {
app.updateColor(args.source, "auto");
}
},
]
}),
// ...
});
All color menu items change the color by calling updateColor()
JavaScript function:
const app = {
async updateColor(e, color) {
const params = {
id: e.id(),
color: color
};
const {data} = await DayPilot.Http.post('/api/events/setColor', params);
e.data.color = color;
calendar.events.update(e);
},
};
The updateColor()
function invokes /api/events/setColor
web service endpoint which is implemented in CalendarController
class (in the Spring Boot backend):
package org.daypilot.demo.html5eventcalendarspring.controller;
// ...
@RestController
public class CalendarController {
@Autowired
EventRepository er;
@PostMapping("/api/events/setColor")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@Transactional
Event setColor(@RequestBody SetColorParams params) {
Event e = er.findOne(params.id);
e.setColor(params.color);
er.save(e);
return e;
}
public static class SetColorParams {
public Long id;
public String color;
}
// ...
}
The color
property is saved as part of the Event
entity class:
package org.daypilot.demo.html5eventcalendarspring.domain;
import java.time.LocalDateTime;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
@Entity
public class Event {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
Long id;
String text;
LocalDateTime start;
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; }
}
We need to apply the color as the event background color on the client side during rendering. We will use onBeforeEventRender
event handler to map the color
property of the data item to the backColor
property that specified the background color.
calendar.onBeforeEventRender = (args) => {
args.data.backColor = args.data.color;
// ...
};
Spring Boot Project Configuration
This is the pom.xml
file that contains the configuration of our Spring Boot application that displays an interactive monthly calendar:
<?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.odemo</groupId>
<artifactId>html5-monthly-calendar-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</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>22</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>
The application.properties
file sets the database provider and configures the database initialization.
spring.datasource.url=jdbc:h2:mem:mydb
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=false
spring.jpa.defer-datasource-initialization=true
spring.jackson.serialization.write_dates_as_timestamps=false
server.port=${port:8081}
History
June 21, 2024: Upgraded to Spring Boot 3.3.1, Java 22, DayPilot Lite for JavaScript 2024.2.532
September 9, 2023: Upgraded to DayPilot Lite 2023.3.494
December 8, 2022: Upgraded to Spring Boot 3.0, switched to the open-source DayPilot Lite library (version 2022.4.438), using ES6 syntax, styling updates
December 18, 2020: Upgraded to Spring Boot 2.4, DayPilot Pro 2020.4.4818; jQuery dependency removed; styling updates
October 2, 2017: Initial release, Spring Boot 1.5