68

Vue Scheduler: Build a Reservation Application in 5 Minutes

 3 years ago
source link: https://code.daypilot.org/69423/vue-js-scheduler-build-a-reservation-application-in-5-minut
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Features

  • Vue scheduler component created using DayPilot JavaScript Scheduler

  • The scheduler component displays a hierarchy of resources on the vertical axis

  • The timeline is displayed on the horizontal axis

  • The appearance and behavior can be customized easily

  • Full drag and drop support

  • The Vue.js project was generated using Scheduler UI Builder

  • Complete Vue.js application is available for download

  • Includes a trial version of DayPilot Pro for JavaScript (see License below)

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.

TLDR: Skip This Tutorial and Generate a Customized Vue.js Project

You can use the online Scheduler UI Builder application to create a customized Scheduler configuration using a visual configurator tool with live preview. Generate and download a complete Vue 2 or Vue 3 project with all dependencies.

NPM Dependencies

First, add daypilot-pro-vue package from npm.daypilot.org to your Vue application:

yarn add https://npm.daypilot.org/daypilot-pro-vue/trial/2020.4.4788.tar.gz

Vue Application

The first version of our Vue scheduler application is very simple. We display the Scheduler Vue component that we will create in the next step:

App.vue

<template>
  <div id="app">
    <Scheduler />
  </div>
</template>

<script>
import Scheduler from './components/Scheduler.vue'

export default {
  name: 'app',
  components: {
    Scheduler
  }
}
</script>

Vue Scheduler Component

In this step, we will create a new Scheduler Vue.js component. Our component will use DayPilotScheduler from daypilot-pro-vue package to render the scheduler UI in the browser.

Template:

<template>
  <DayPilotScheduler id="dp" :config="config" ref="scheduler" />
</template>

JavaScript:

<script>
import {DayPilot, DayPilotScheduler} from 'daypilot-pro-vue'

export default {
  name: 'Scheduler',
  data: function() {
    return {
      config: {},
    }
  },
  components: {
    DayPilotScheduler
  },
}
</script>

The component template contains an <DayPilotScheduler> tag with two properties:

  • The id attribute (required) specifies the Scheduler id. This id will be used for the Scheduler DOM element.

  • The config attribute specifies a JavaScript object with the initial Scheduler configuration.

Initial Vue.js Scheduler Configuration

vue-js-scheduler-reservation-application-initial-configuration.png

We have added an empty Scheduler to our Vue.js application. It uses the default configuration and the view doesn’t display much. In this step, we will use the config property to customize the Scheduler appearance.

You can use the config object to set the Scheduler properties and event handlers. This first version specifies only basic properties that define the Scheduler timeline (timeHeader, scale, days, startDate).

JavaScript

<script>
import {DayPilot, DayPilotScheduler} from 'daypilot-pro-vue'

export default {
  name: 'Scheduler',
  data: function() {
    return {
      config: {
        timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
        scale: "Day",
        days: DayPilot.Date.today().daysInMonth(),
        startDate: DayPilot.Date.today().firstDayOfMonth(),
      },
    }
  },
  components: {
    DayPilotScheduler
  }
}
</script>

Accessing the Scheduler Object in Vue

The Scheduler component uses DayPilot.Scheduler internally to create the component. We need a reference to the DayPilot.Scheduler object in order to call its methods using the JavaScript API.

First, we need a reference to the Vue.js scheduler object. We can get it using the ref attribute of the <DayPilotScheduler> element:

<DayPilotScheduler id="dp" :config="config" ref="scheduler" />

Now we can reach the Vue.js component using $refs:

const component = this.$refs.scheduler;

The DayPilotScheduler Vue.js component stores the internal DayPilot.Scheduler instance in control property. To make the access easier, we will add a new computed property to our Vue.js application:

computed: {
  scheduler: function () {
    return this.$refs.scheduler.control;
  }
},

Now we can use this.scheduler property to reach the DayPilot.Scheduler object:

mounted: function() {
  this.scheduler.message("Welcome!");
}

Loading Vue.js Scheduler Resources

vue-js-scheduler-reservation-application-resources.png

In this step, we will create loadResources() method that loads the resource data and updates the Scheduler:

methods: {
  loadResources: function() {
    // placeholder for an AJAX call
    var data = [
      { name: "Group A", id: "GA", expanded: true, children: [
        {name: "Resource 1", id: "R1"},
        {name: "Resource 2", id: "R2"},
        {name: "Resource 3", id: "R3"},
        {name: "Resource 4", id: "R4"},
      ]},
      { name: "Group B", id: "GB", expanded: true, children: [
        {name: "Resource 5", id: "R5"},
        {name: "Resource 6", id: "R6"},
        {name: "Resource 7", id: "R7"},
        {name: "Resource 8", id: "R8"},
      ]}
    ];
    this.scheduler.update({resources: data});
  },
  // ...
}

Loading Vue.js Scheduler Reservations

vue-js-scheduler-reservation-application-loading-reservations.png

Now we will create loadEvents() method that loads the event data to the Scheduler using update() call. Normally, this method should load the data from the server side using an HTTP call but we will return a static array in this example:

methods: {
  loadEvents: function() {
    // placeholder for an AJAX call
    var data = [
      {
        id: 1,
        resource: "R1",
        start: DayPilot.Date.today().firstDayOfMonth().addDays(3),
        end: DayPilot.Date.today().firstDayOfMonth().addDays(7),
        text: "Event 1"
      },
      {
        id: 2,
        resource: "R1",
        start: DayPilot.Date.today().firstDayOfMonth().addDays(10),
        end: DayPilot.Date.today().firstDayOfMonth().addDays(13),
        text: "Event 2"
      }
    ];
    this.scheduler.update({events: data});
  },
  // ...
},

Loading the Data during App Initialization

Now we need to add the loading methods to the mounted event so they will be called during the Vue.js app initialization:

mounted: function() {
  this.loadResources();
  this.loadEvents();
}

Reservation Context Menu

vue-js-scheduler-reservation-application-context-menu.png

In this step, we will add a context menu to the reservations. The context menu will let users change the reservation color and delete it.

The context menu can be defined using contextMenu config property. Users can activate the context menu by right-clicking the reservation.

We will also add an icon to the reservation upper-left corner that will serve as a hint that a context menu is available. The icon can be added by inserting an active area using onBeforeEventRender event handler.

export default {
  name: 'Scheduler',
  data: function() {
    return {
      config: {
        // ...
        onBeforeEventRender: 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: args => {
                var e = args.source;
                this.scheduler.events.remove(e);
                this.scheduler.message("Deleted.");
              }
            },
            {
              text: "-"
            },                        {
              text: "Blue",
              icon: "icon icon-blue",
              color: "#1155cc",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },
            {
              text: "Green",
              icon: "icon icon-green",
              color: "#6aa84f",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },
            {
              text: "Yellow",
              icon: "icon icon-yellow",
              color: "#f1c232",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },
            {
              text: "Red",
              icon: "icon icon-red",
              color: "#cc0000",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },

          ]
        })
      },
    }
  },
  // ...
}

The updateColor() method is defined in the methods section. It changes the reservation data object properties (color) and calls the Scheduler API to update the UI.

export default {
  // ...
  methods: {
    // ...
    updateColor(e, color) {
      var dp = this.scheduler;
      e.data.color = color;
      dp.events.update(e);
      dp.message("Color updated");
    }
  },
  // ...
}

Complete Vue App Source Code

Here you can find the full source code of our Vue.js application. 

Scheduler.vue

<template>
  <DayPilotScheduler id="dp" :config="config" ref="scheduler" />
</template>

<script>
import {DayPilot, DayPilotScheduler} from 'daypilot-pro-vue'

export default {
  name: 'Scheduler',
  data: function() {
    return {
      config: {
        timeHeaders: [{groupBy: "Month"}, {groupBy: "Day", format: "d"}],
        scale: "Day",
        days: DayPilot.Date.today().daysInMonth(),
        startDate: DayPilot.Date.today().firstDayOfMonth(),
        timeRangeSelectedHandling: "Enabled",
        eventHeight: 40,
        onTimeRangeSelected: args => {
          var dp = this.scheduler;
          DayPilot.Modal.prompt("Create a new event:", "Event 1").then(modal => {
            dp.clearSelection();
            if (modal.canceled) {
              return;
            }
            dp.events.add({
              start: args.start,
              end: args.end,
              id: DayPilot.guid(),
              resource: args.resource,
              text: modal.result
            });
          });
        },
        eventMoveHandling: "Update",
        onEventMoved: args => {
          this.message("Event moved: " + args.e.data.text);
        },
        eventResizeHandling: "Update",
        onEventResized: args => {
          this.message("Event resized: " + args.e.data.text);
        },
        eventClickHandling: "Enabled",
        onEventClicked: args => {
          this.message("Event clicked: " + args.e.data.text);
        },
        eventHoverHandling: "Disabled",
        treeEnabled: true,
        onBeforeEventRender: 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: args => {
                var e = args.source;
                this.scheduler.events.remove(e);
                this.scheduler.message("Deleted.");
              }
            },
            {
              text: "-"
            },                        {
              text: "Blue",
              icon: "icon icon-blue",
              color: "#1155cc",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },
            {
              text: "Green",
              icon: "icon icon-green",
              color: "#6aa84f",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },
            {
              text: "Yellow",
              icon: "icon icon-yellow",
              color: "#f1c232",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },
            {
              text: "Red",
              icon: "icon icon-red",
              color: "#cc0000",
              onClick: args => { this.updateColor(args.source, args.item.color); }
            },

          ]
        })
      },
    }
  },
  props: {
  },
  components: {
    DayPilotScheduler
  },
  computed: {
    // DayPilot.Scheduler object - https://api.daypilot.org/daypilot-scheduler-class/
    scheduler: function () {
      return this.$refs.scheduler.control;
    }
  },
  methods: {
    loadReservations() {
      // placeholder for an AJAX call
      var data = [
        {
          id: 1,
          resource: "R1",
          start: DayPilot.Date.today().firstDayOfMonth().addDays(3),
          end: DayPilot.Date.today().firstDayOfMonth().addDays(7),
          text: "Event 1",
          color: "#1155cc"
        },
        {
          id: 2,
          resource: "R1",
          start: DayPilot.Date.today().firstDayOfMonth().addDays(9),
          end: DayPilot.Date.today().firstDayOfMonth().addDays(12),
          text: "Event 2",
          color: "#6aa84f"
        },
        {
          id: 3,
          resource: "R3",
          start: DayPilot.Date.today().firstDayOfMonth().addDays(3),
          end: DayPilot.Date.today().firstDayOfMonth().addDays(5),
          text: "Event 3",
          color: "#1155cc"
        },
        {
          id: 4,
          resource: "R3",
          start: DayPilot.Date.today().firstDayOfMonth().addDays(5),
          end: DayPilot.Date.today().firstDayOfMonth().addDays(7),
          text: "Event 4",
          color: "#6aa84f"
        },
        {
          id: 5,
          resource: "R3",
          start: DayPilot.Date.today().firstDayOfMonth().addDays(7),
          end: DayPilot.Date.today().firstDayOfMonth().addDays(9),
          text: "Event 5",
          color: "#f1c232"
        },
        {
          id: 6,
          resource: "R3",
          start: DayPilot.Date.today().firstDayOfMonth().addDays(9),
          end: DayPilot.Date.today().firstDayOfMonth().addDays(11),
          text: "Event 6",
          color: "#cc0000"
        },
      ];
      this.scheduler.update({events: data});
    },
    loadResources() {
      // placeholder for an AJAX call
      var data = [
        {
          name: "Group A", id: "GA", expanded: true, children: [
            {name: "Resource 1", id: "R1"},
            {name: "Resource 2", id: "R2"},
            {name: "Resource 3", id: "R3"},
            {name: "Resource 4", id: "R4"},
          ]
        },
        {
          name: "Group B", id: "GB", expanded: true, children: [
            {name: "Resource 5", id: "R5"},
            {name: "Resource 6", id: "R6"},
            {name: "Resource 7", id: "R7"},
            {name: "Resource 8", id: "R8"},
          ]
        }
      ];
      this.scheduler.update({resources: data});
    },
    updateColor(e, color) {
      var dp = this.scheduler;
      e.data.color = color;
      dp.events.update(e);
      dp.message("Color updated");
    }
  },
  mounted: function() {
    this.loadResources();
    this.loadReservations();

    this.scheduler.message("Welcome!");
  }
}
</script>

History

  • December 5, 2020: Using package-based Vue 2 project, upgraded to DayPilot Pro for JavaScript 2020.4.4788

  • September 24, 2019: Upgraded to DayPilot Pro for JavaScript 2019.3.4012

  • February 9, 2018: Context menu added

  • February 5, 2018: Initial release


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK