Overview

  • Vue.js appointment calendar created using DayPilot JavaScript calendar component
  • How to create and configure the Vue.js calendar
  • How to load event/appointment data
  • How to add support for creating new appointment using drag and drop
  • This project was generated using Calendar UI Builder

License

Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.

Vue.js Calendar Component

vue-js-appointment-calendar-component-initialization.png

Let's start with a blank Vue.js project. One of the big advantages of Vue.js is the ability to integrate with existing HTML and JavaScript. The setup is very simple and it doesn't require complicated boilerplate code:

<script src="https://unpkg.com/vue"></script>

<div id="calendar-app">
</div>

<script>
    var app = new Vue({
        el: '#calendar-app',
        mounted: function () {
            console.log("The first version of our Vue.js calendar application works!");
        }
    });
</script>

That was easy, but our web application is not doing anything yet. So let's create a new Vue.js component that will display the appointment calendar using DayPilot JavaScript Calendar.

The calendar component needs to be registered before we initialize the Vue.js applicaiton

<script>

Vue.component('calendar', {
    props: ['id', 'config'],
    template: '<div :id="id"></div>',
    mounted: function () {
        this.control = new DayPilot.Calendar(this.id, this.config).init();
    }
});

var app = new Vue({
    el: '#calendar-app',
    mounted: function () {
        console.log("The first version of our Vue.js calendar application works!");
    }
});

</script>

As soon as the calendar component is registered, you can add it to the HTML using <calendar> tag:

<div id="calendar-app">
  <calendar id="dp" :config="initConfig" ref="calendar"></calendar>
</div>

If we run the application now we can see a simple calendar view with the default configuration (empty day view).

Vue.js Weekly Calendar Configuration

vue.js-weekly-calendar-component.png

By default, the calendar display a day view. We want to switch it to a week view and this can be done by setting the viewType property to "Week". 

The calendar properties can be specified using the :config attribute of the component. We have set the value to "initConfig" which tells the calendar to look for initConfig object in the application data.

var app = new Vue({
  el: '#calendar-app',
  data: {
      initConfig: {
          viewType: "Week"
      }
  },
});

You can use the initConfig object to set additional calendar properties and event handlers.

Loading Appointments

vue.js-weekly-calendar-component-loading-events.png

In order to load events we need to access the calendar object (DayPilot.Calendar class) and use its API. This object is saved as control property of the Vue.js component.

To make it easily accessible, we will add a calendar computed property to our Vue.js application. It finds the calendar Vue.js component using "ref" attribute and returns the DayPilot.Calendar object.

var app = new Vue({
    el: '#calendar-app',
    data: {
        initConfig: {
            viewType: "Week"
        }
    },
    computed: {
       // DayPilot.Calendar object
       // https://api.daypilot.org/daypilot-calendar-class/
       calendar: function () {
         return this.$refs.calendar.control;
       }
    },

    // ...
});

The appointment data can be displayed using update() method. The update() methods accepts an object with calendar properties to be updated. It loads the properties and redraws the calendar UI.

var app = new Vue({
    el: '#calendar-app',
    data: {
        initConfig: {
            viewType: "Week"
        }
    },
    computed: {
       // DayPilot.Calendar object
       // https://api.daypilot.org/daypilot-calendar-class/
       calendar: function () {
         return this.$refs.calendar.control;
       }
    },
    methods: {
        loadEvents: function () {
            // placeholder for an AJAX call
            var data = [
                {
                    id: 1,
                    start: DayPilot.Date.today().addHours(10),
                    end: DayPilot.Date.today().addHours(11),
                    text: "Event 1"
                },
                {
                    id: 2,
                    start: DayPilot.Date.today().addHours(13),
                    end: DayPilot.Date.today().addHours(16),
                    text: "Event 2"
                }
            ];
            this.calendar.update({events: data});
        }
    },
    mounted: function () {
        this.loadEvents();
    }
});

Creating New Appointments

vue.js-weekly-calendar-component-create-event.png

The JavaScript calendar lets users users create new appointments using drag and drop time range selection.

To handle the user action, we'll add onTimeRangeSelected event handler to the config object.

var app = new Vue({
    el: '#calendar-app',
    data: {
        initConfig: {
            viewType: "Week",
            timeRangeSelectedHandling: "Enabled",
            onTimeRangeSelected: function (args) {
                DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function (modal) {
                    var dp = args.control;
                    dp.clearSelection();
                    if (!modal.result) {
                        return;
                    }
                    dp.events.add(new DayPilot.Event({
                        start: args.start,
                        end: args.end,
                        id: DayPilot.guid(),
                        text: modal.result
                    }));
                });
            },
            // ...
        }
    },
    
    // ...
    
});

Calendar Appointment Context Menu

vue.js-weekly-calendar-component-context-menu.png

In order to allow additional actions to be performed we will add a context menu to the calendar appointments. It will let users delete the appointment or change the highlight color.

First, we need to define the context menu. It will display a Delete command, a separator, and four item that will allow color changes:

contextMenu: new DayPilot.Menu({
  items: [
      {
          text: "Delete",
          onClick: function (args) {
              var e = args.source;
              app.calendar.events.remove(e);
              app.calendar.message("Deleted.");
          }
      },
      {
          text: "-"
      }, {
          text: "Blue",
          icon: "icon icon-blue",
          color: "#1155cc",
          onClick: function (args) {
              app.updateColor(args.source, args.item.color);
          }
      },
      {
          text: "Green",
          icon: "icon icon-green",
          color: "#6aa84f",
          onClick: function (args) {
              app.updateColor(args.source, args.item.color);
          }
      },
      {
          text: "Yellow",
          icon: "icon icon-yellow",
          color: "#f1c232",
          onClick: function (args) {
              app.updateColor(args.source, args.item.color);
          }
      },
      {
          text: "Red",
          icon: "icon icon-red",
          color: "#cc0000",
          onClick: function (args) {
              app.updateColor(args.source, args.item.color);
          }
      },

  ]
})

The menu defined using contextMenu property will be linked to appointments. Our users can activate it by right-clicking the appointment.

However, it's not obvious that the context menu is available so we'll add a visual hint that will appear when the users hover over an appointment. We can add this hint using an active area - a custom object with predefined location and appearance that will appear on hover. We will add the active area using onBeforeEventRender event handler:

onBeforeEventRender: function (args) {
    args.data.barColor = args.data.color;
    args.data.areas = [
        {
            top: 6,
            right: 2,
            icon: "icon-triangle-down",
            visibility: "Hover",
            action: "ContextMenu",
            style: "font-size: 12px; background-color: #f9f9f9; border: 1px solid #ccc; padding: 2px 2px 0px 2px; cursor:pointer;"
        }
    ];
},

Complete Source Code of the Vue.js Application

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Vue.js Weekly Calendar Tutorial</title>

    <link href="index.css" rel="stylesheet" type="text/css">
    <link href="icons/style.css" rel="stylesheet" type="text/css">

    <!-- DayPilot library -->
    <script src="js/daypilot/daypilot-all.min.js"></script>

    <!-- Vue.js -->
    <script src="https://unpkg.com/vue"></script>

</head>
<body>

<div class="header">
    <h1><a href='https://code.daypilot.org/10748/vue-js-weekly-calendar-tutorial'>Vue.js Weekly Calendar Tutorial</a></h1>
    <div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - AJAX Calendar/Scheduling Widgets for JavaScript/HTML5/jQuery/AngularJS</div>
</div>

<div class="main">

    <div id="calendar-app">
        <calendar id="dp" :config="initConfig" ref="calendar"></calendar>
    </div>

</div>

<script>
    Vue.component('calendar', {
        props: ['id', 'config'],
        template: '<div :id="id"></div>',
        mounted: function () {
            this.control = new DayPilot.Calendar(this.id, this.config).init();
        }
    });

    var app = new Vue({
        el: '#calendar-app',
        data: {
            initConfig: {
                viewType: "Week",
                timeRangeSelectedHandling: "Enabled",
                onTimeRangeSelected: function (args) {
                    DayPilot.Modal.prompt("Create a new event:", "Event 1").then(function (modal) {
                        var dp = args.control;
                        dp.clearSelection();
                        if (!modal.result) {
                            return;
                        }
                        dp.events.add(new DayPilot.Event({
                            start: args.start,
                            end: args.end,
                            id: DayPilot.guid(),
                            text: modal.result
                        }));
                    });
                },
                eventDeleteHandling: "Disabled",
                onEventMoved: function (args) {
                    this.message("Event moved");
                },
                onEventResized: function (args) {
                    this.message("Event resized");
                },
                onBeforeEventRender: function (args) {
                    args.data.barColor = args.data.color;
                    args.data.areas = [
                        {
                            top: 6,
                            right: 2,
                            icon: "icon-triangle-down",
                            visibility: "Hover",
                            action: "ContextMenu",
                            style: "font-size: 12px; background-color: #f9f9f9; border: 1px solid #ccc; padding: 2px 2px 0px 2px; cursor:pointer;"
                        }
                    ];
                },
                contextMenu: new DayPilot.Menu({
                    items: [
                        {
                            text: "Delete",
                            onClick: function (args) {
                                var e = args.source;
                                app.calendar.events.remove(e);
                                app.calendar.message("Deleted.");
                            }
                        },
                        {
                            text: "-"
                        }, {
                            text: "Blue",
                            icon: "icon icon-blue",
                            color: "#1155cc",
                            onClick: function (args) {
                                app.updateColor(args.source, args.item.color);
                            }
                        },
                        {
                            text: "Green",
                            icon: "icon icon-green",
                            color: "#6aa84f",
                            onClick: function (args) {
                                app.updateColor(args.source, args.item.color);
                            }
                        },
                        {
                            text: "Yellow",
                            icon: "icon icon-yellow",
                            color: "#f1c232",
                            onClick: function (args) {
                                app.updateColor(args.source, args.item.color);
                            }
                        },
                        {
                            text: "Red",
                            icon: "icon icon-red",
                            color: "#cc0000",
                            onClick: function (args) {
                                app.updateColor(args.source, args.item.color);
                            }
                        },

                    ]
                })
            }
        },
        computed: {
            // DayPilot.Calendar object
            // https://api.daypilot.org/daypilot-calendar-class/
            calendar: function () {
                return this.$refs.calendar.control;
            }
        },
        methods: {
            loadEvents: function () {
                // placeholder for an AJAX call
                var data = [
                    {
                        id: 1,
                        start: DayPilot.Date.today().addHours(10),
                        end: DayPilot.Date.today().addHours(11),
                        text: "Event 1"
                    },
                    {
                        id: 2,
                        start: DayPilot.Date.today().addHours(13),
                        end: DayPilot.Date.today().addHours(16),
                        text: "Event 2"
                    }
                ];
                this.calendar.update({events: data});
            },
            updateColor: function(e, color) {
                var dp = this.calendar;
                e.data.color = color;
                dp.events.update(e);
                dp.message("Color updated");
            }
        },
        mounted: function () {
            this.loadEvents();
        }
    });
</script>
</body>
</html>

History

  • September 18, 2019: Upgraded to DayPilot Pro for JavaScript 2019.3.4012. Context menu added.
  • April 11, 2018: Initial release