Features

  • Java 20

  • Spring Boot 3.0

  • 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

spring boot monthly calendar open source component

This is 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

spring boot monthly calendar open source load events http

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

spring boot monthly calendar open source 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

spring boot monthly calendar open source change event color

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;
  // ...
};

History

  • 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