---
title: "Vue Event calendar Drag-and-Drop Task Presets Example | Mobiscroll"
component: "Event calendar"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/eventcalendar/external-event-presets"
---

# Drag-and-Drop Task Presets - Vue Event calendar demo

Drag preset task templates from an external list onto a Vue event calendar — a popup form captures task details and technician at drop time. For Vue and Nuxt.

- [Live demo](https://demo.mobiscroll.com/vue/eventcalendar/external-event-presets)
- [View SFC JS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue/tree/main/src/demos/eventcalendar/calendar-view/external-event-presets)
- [View SFC TS code on GitHub](https://github.com/acidb/mobiscroll-demos-vue-ts/tree/main/src/demos/eventcalendar/calendar-view/external-event-presets)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/eventcalendar/external-event-presets.md), [jQuery](https://demo.mobiscroll.com/jquery/eventcalendar/external-event-presets.md), [Angular](https://demo.mobiscroll.com/angular/eventcalendar/external-event-presets.md), [React](https://demo.mobiscroll.com/react/eventcalendar/external-event-presets.md)

## Description

Use external draggable events to create preset tasks that people can quickly copy and spawn events from. A great example is a car wrapping shop where different jobs need to be set up depending on the type of vehicle.

Print a list of predefined tasks and initialize them as `draggable` elements. Enable [`externalDrop`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-externalDrop) for the calendar and set up the predefined event skeletons for the [draggable](https://mobiscroll.com/docs/vue/eventcalendar/drag-and-drop#targeting-the-eventcalendar) component.

Whenever there is more information to be captured, like detailed task notes, customer information, the name of the technician... you can trigger a `popup` with the task form in the [`event-create`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreate) lifecycle event. For failed drops (trying to schedule a task during lunch breaks or weekends) the [`event-create-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreateFailed) event will be triggered.

**Want to see how external presets look & feel for a scheduler?** [Check out this example →](https://demo.mobiscroll.com/vue/scheduler/external-event-presets)

## What this demo shows

- A desktop month view event calendar paired with an external list of predefined tasks that can be dragged onto the calendar.
- **Calendar layout** The main view shows a full month grid with no events.
- **Calendar header** The header shows the current month and year on the left, and blue month navigation arrows with a `Today` button between them on the right.
- **Invalid dates** Weekend days are disabled, so they cannot be used as drop targets for new tasks. When a user tries to drop an event in a restricted cell, a `Can't create event on this date` toast appears at the bottom center of the calendar.
- **External task list** A right-side panel titled `Available tasks` displays preset items: `Small wrap 2 day`, `Full-size wrap 3 days`, `Mid-size wrap 3 days`, `Roadster wrap 3 days`, `SUV wrap 4 days`, and `Hypercar wrap 5 days`.
- **External drag & drop** Tasks can be dragged from the external list and dropped onto a calendar day to schedule predefined events.
- **Created event display** Dropping the task opens a popup below the event label with the title `Assign task`.
- **Popup form** The popup includes a `Task` text field prefilled with the task title, a `Details` text field with the `Add description…` placeholder, and a `Technician` select with the `Please select…` placeholder.
- **Popup confirmation** The popup footer contains a blue `Ok` button in the bottom-right corner to confirm the event details.
- **Scheduled task** After a successful drop and confirmation the task label appears inside day cell and a confirmation toast appears on the bottom center part of the calendar with this message `New task added`.
- **Day cell states** Hovering a day cell highlights the day number with a gray background, while clicking the empty part of the cell selects the day and highlights the day number with a blue background.
- **Gesture navigation** The month view can also be changed by clicking and dragging the calendar left or right.

## Best for

- **Service scheduling** Planning repeatable job types, such as vehicle wrapping tasks, from predefined task templates.
- **Template-based booking** Letting teams create new calendar events by dragging standard task presets onto available dates.
- **Technician assignment workflows** Capturing extra scheduling details, such as notes and technician selection, at the moment a task is placed.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-themeVariant) - option
- [`labels`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-labels) - option
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`invalid`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-invalid) - option
- [`dragToMove`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToMove) - option
- [`externalDrop`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-externalDrop) - option
- [`event-created`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreated) - event
- [`event-create-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreateFailed) - event
- [`event-update-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventUpdateFailed) - event
- [`display`](https://mobiscroll.com/docs/vue/popup/api#opt-display) - option
- [`headerText`](https://mobiscroll.com/docs/vue/popup/api#opt-headerText) - option
- [`width`](https://mobiscroll.com/docs/vue/popup/api#opt-width) - option
- [`touchUi`](https://mobiscroll.com/docs/vue/popup/api#opt-touchUi) - option
- [`buttons`](https://mobiscroll.com/docs/vue/popup/api#opt-buttons) - option
- [`anchor`](https://mobiscroll.com/docs/vue/popup/api#opt-anchor) - option
- [`isOpen`](https://mobiscroll.com/docs/vue/popup/api#opt-isOpen) - option
- [`close`](https://mobiscroll.com/docs/vue/popup/api#event-onClose) - event
- [`display`](https://mobiscroll.com/docs/vue/select/api#opt-display) - option
- [`label`](https://mobiscroll.com/docs/vue/select/api#opt-label) - option
- [`data`](https://mobiscroll.com/docs/vue/select/api#opt-data) - option
- [`touchUi`](https://mobiscroll.com/docs/vue/select/api#opt-touchUi) - option
- [`change`](https://mobiscroll.com/docs/vue/select/api#event-onChange) - event
- [`close`](https://mobiscroll.com/docs/vue/notifications/toast#event-onClose) - event

## Code

### SFC JS

```vue
<script setup>
import {
  MbscDraggable,
  MbscEventcalendar,
  MbscInput,
  MbscPage,
  MbscPopup,
  MbscSelect,
  MbscTextarea,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import { ref } from 'vue'

setOptions({
  theme: 'ios',
  themeVariant: 'light'
})

const myTasks = ref([
  {
    title: 'Small wrap',
    color: '#637e57',
    start: '2026-10-25T00:00',
    end: '2026-10-26T00:00',
    length: '2 days'
  },
  {
    title: 'Full-size wrap',
    color: '#50789d',
    start: '2026-10-25T00:00',
    end: '2026-10-27T00:00',
    length: '3 days'
  },
  {
    title: 'Mid-size wrap',
    color: '#6c5d45',
    start: '2026-10-25T00:00',
    end: '2026-10-27T00:00',
    length: '3 days'
  },
  {
    title: 'Roadster wrap',
    color: '#9da721',
    start: '2026-10-25T00:00',
    end: '2026-10-27T00:00',
    length: '3 days'
  },
  {
    title: 'SUV wrap',
    color: '#cd6957',
    start: '2026-10-25T00:00',
    end: '2026-10-28T00:00',
    length: '4 days'
  },
  {
    title: 'Hypercar wrap',
    color: '#7a5886',
    start: '2026-10-25T00:00',
    end: '2026-10-29T00:00',
    length: '5 days'
  }
])

const myData = ref([
  { value: '1', text: 'Roly Chester' },
  { value: '2', text: 'Tucker Wayne' },
  { value: '3', text: 'Baker Brielle' },
  { value: '4', text: 'Jami Walter' },
  { value: '5', text: 'Patrick Toby' },
  { value: '6', text: 'Tranter Logan' },
  { value: '7', text: 'Payton Sinclair' }
])

const myView = {
  calendar: { labels: true }
}

const myInvalid = [
  {
    recurring: {
      repeat: 'weekly',
      weekDays: 'SA,SU'
    }
  }
]

const dragElements = ref([])
const title = ref('')
const details = ref('')
const technician = ref('')
const popupAnchor = ref(null)
const isPopupOpen = ref(false)
const toastMessage = ref('')
const isToastOpen = ref(false)

function fillDialog(args) {
  const event = args.event
  title.value = event.title
  details.value = event.details
  technician.value = event.technician
  popupAnchor.value = args.target
  isPopupOpen.value = true
}

function handleEventCreated(args) {
  fillDialog(args)
}

function handleEventCreateFailed() {
  toastMessage.value = "Can't create event on this date"
  isToastOpen.value = true
}

function handleEventUpdateFailed() {
  toastMessage.value = "Can't add event on this date"
  isToastOpen.value = true
}

function handlePopupClose() {
  isPopupOpen.value = false
  toastMessage.value = 'New task added'
  isToastOpen.value = true
}

function handleSelectChange(event) {
  technician.value = event.value
}

function handleToastClose() {
  isToastOpen.value = false
}
</script>

<template>
  <MbscPage>
    <div class="mbsc-grid mbsc-no-padding">
      <div class="mbsc-row">
        <div class="mbsc-col-sm-9 external-event-calendar">
          <MbscEventcalendar
            :view="myView"
            :invalid="myInvalid"
            :dragToMove="true"
            :externalDrop="true"
            @event-created="handleEventCreated"
            @event-create-failed="handleEventCreateFailed"
            @event-update-failed="handleEventUpdateFailed"
          />
        </div>
        <div class="mbsc-col-sm-3">
          <div class="mbsc-form-group-title">Available tasks</div>
          <div v-for="(task, i) in myTasks" :key="task.id">
            <div ref="dragElements" class="external-event-task" :style="{ background: task.color }">
              <div>{{ task.title }}</div>
              <div>{{ task.length }}</div>
              <MbscDraggable :element="dragElements[i]" :dragData="task" />
            </div>
          </div>
        </div>
        <MbscPopup
          display="anchored"
          headerText="Assign task"
          :width="400"
          :contentPadding="false"
          :touchUi="false"
          :buttons="['ok']"
          :anchor="popupAnchor"
          :isOpen="isPopupOpen"
          @close="handlePopupClose"
        >
          <div class="mbsc-form-group">
            <MbscInput label="Task" :defaultValue="title" readOnly></MbscInput>
            <MbscTextarea
              label="Details"
              :defaultValue="details"
              placeholder="Add description..."
            ></MbscTextarea>
            <MbscSelect
              display="anchored"
              label="Technician"
              placeholder="Please select..."
              :data="myData"
              :value="technician"
              :touchUi="false"
              @change="handleSelectChange"
            />
          </div>
        </MbscPopup>
      </div>
      <MbscToast :message="toastMessage" :isOpen="isToastOpen" @close="handleToastClose" />
    </div>
  </MbscPage>
</template>

<style>
.external-event-calendar {
  border-right: 1px solid #ccc;
}

.external-event-task {
  color: #fff;
  padding: 10px;
  margin: 20px;
  border-radius: 8px;
  font-family:
    -apple-system,
    Segoe UI,
    Roboto,
    sans-serif;
}

.demo-external-event-presets.demo-wrapper,
.demo-external-event-presets .mbsc-grid,
.demo-external-event-presets .mbsc-row,
.demo-external-event-presets .external-event-calendar {
  height: 100%;
}
</style>
```

### SFC TS

```vue
<script setup lang="ts">
import {
  MbscDraggable,
  MbscEventcalendar,
  MbscInput,
  MbscPage,
  MbscPopup,
  MbscSelect,
  MbscTextarea,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import type {
  MbscCalendarEvent,
  MbscEventcalendarView,
  MbscEventCreatedEvent
} from '@mobiscroll/vue'
import { ref } from 'vue'

setOptions({
  theme: 'ios',
  themeVariant: 'light'
})

const myTasks = ref<MbscCalendarEvent[]>([
  {
    title: 'Small wrap',
    color: '#637e57',
    start: '2026-10-25T00:00',
    end: '2026-10-26T00:00',
    length: '2 days'
  },
  {
    title: 'Full-size wrap',
    color: '#50789d',
    start: '2026-10-25T00:00',
    end: '2026-10-27T00:00',
    length: '3 days'
  },
  {
    title: 'Mid-size wrap',
    color: '#6c5d45',
    start: '2026-10-25T00:00',
    end: '2026-10-27T00:00',
    length: '3 days'
  },
  {
    title: 'Roadster wrap',
    color: '#9da721',
    start: '2026-10-25T00:00',
    end: '2026-10-27T00:00',
    length: '3 days'
  },
  {
    title: 'SUV wrap',
    color: '#cd6957',
    start: '2026-10-25T00:00',
    end: '2026-10-28T00:00',
    length: '4 days'
  },
  {
    title: 'Hypercar wrap',
    color: '#7a5886',
    start: '2026-10-25T00:00',
    end: '2026-10-29T00:00',
    length: '5 days'
  }
])

const myData = ref([
  { value: '1', text: 'Roly Chester' },
  { value: '2', text: 'Tucker Wayne' },
  { value: '3', text: 'Baker Brielle' },
  { value: '4', text: 'Jami Walter' },
  { value: '5', text: 'Patrick Toby' },
  { value: '6', text: 'Tranter Logan' },
  { value: '7', text: 'Payton Sinclair' }
])

const myView: MbscEventcalendarView = {
  calendar: { labels: true }
}

const myInvalid = [
  {
    recurring: {
      repeat: 'weekly',
      weekDays: 'SA,SU'
    }
  }
]

const dragElements = ref<any>([])
const title = ref<string>('')
const details = ref<string>('')
const technician = ref<string>('')
const popupAnchor = ref<any>(null)
const isPopupOpen = ref<boolean>(false)
const toastMessage = ref<string>('')
const isToastOpen = ref<boolean>(false)

function fillDialog(args: any) {
  const event = args.event
  title.value = event.title
  details.value = event.details
  technician.value = event.technician
  popupAnchor.value = args.target
  isPopupOpen.value = true
}

function handleEventCreated(args: MbscEventCreatedEvent) {
  fillDialog(args)
}

function handleEventCreateFailed() {
  toastMessage.value = "Can't create event on this date"
  isToastOpen.value = true
}

function handleEventUpdateFailed() {
  toastMessage.value = "Can't add event on this date"
  isToastOpen.value = true
}

function handlePopupClose() {
  isPopupOpen.value = false
  toastMessage.value = 'New task added'
  isToastOpen.value = true
}

function handleSelectChange(event: any) {
  technician.value = event.value
}

function handleToastClose() {
  isToastOpen.value = false
}
</script>

<template>
  <MbscPage>
    <div class="mbsc-grid mbsc-no-padding">
      <div class="mbsc-row">
        <div class="mbsc-col-sm-9 external-event-calendar">
          <MbscEventcalendar
            :view="myView"
            :invalid="myInvalid"
            :dragToMove="true"
            :externalDrop="true"
            @event-created="handleEventCreated"
            @event-create-failed="handleEventCreateFailed"
            @event-update-failed="handleEventUpdateFailed"
          />
        </div>
        <div class="mbsc-col-sm-3">
          <div class="mbsc-form-group-title">Available tasks</div>
          <div v-for="(task, i) in myTasks" :key="task.id">
            <div ref="dragElements" class="external-event-task" :style="{ background: task.color }">
              <div>{{ task.title }}</div>
              <div>{{ task.length }}</div>
              <MbscDraggable :element="dragElements[i]" :dragData="task" />
            </div>
          </div>
        </div>
        <MbscPopup
          display="anchored"
          headerText="Assign task"
          :width="400"
          :contentPadding="false"
          :touchUi="false"
          :buttons="['ok']"
          :anchor="popupAnchor"
          :isOpen="isPopupOpen"
          @close="handlePopupClose"
        >
          <div class="mbsc-form-group">
            <MbscInput label="Task" :defaultValue="title" readOnly></MbscInput>
            <MbscTextarea
              label="Details"
              :defaultValue="details"
              placeholder="Add description..."
            ></MbscTextarea>
            <MbscSelect
              display="anchored"
              label="Technician"
              placeholder="Please select..."
              :data="myData"
              :value="technician"
              :touchUi="false"
              @change="handleSelectChange"
            />
          </div>
        </MbscPopup>
      </div>
      <MbscToast :message="toastMessage" :isOpen="isToastOpen" @close="handleToastClose" />
    </div>
  </MbscPage>
</template>

<style>
.external-event-calendar {
  border-right: 1px solid #ccc;
}

.external-event-task {
  color: #fff;
  padding: 10px;
  margin: 20px;
  border-radius: 8px;
  font-family:
    -apple-system,
    Segoe UI,
    Roboto,
    sans-serif;
}

.demo-external-event-presets.demo-wrapper,
.demo-external-event-presets .mbsc-grid,
.demo-external-event-presets .mbsc-row,
.demo-external-event-presets .external-event-calendar {
  height: 100%;
}
</style>
```

## Implementation notes

- Set `view: { calendar: { labels: true } }`. Enable `externalDrop: true` to accept drops from outside the calendar and `dragToMove: true` to allow moving already-placed events. Block weekends with `invalid: [{ recurring: { repeat: 'weekly', weekDays: 'SA,SU' } }]`.
- Define each preset task as an object with `title`, `color`, `start`, and `end` fields (where `start`/`end` represent today-relative offsets that set the event duration when dropped). Render the task list outside the calendar and initialize each item as a draggable element using the `Draggable` component with `dragData` set to the task object. Angular: use the `mbsc-draggable` directive with `[dragData]`. JS/jQuery: call `mobiscroll.draggable(element, { dragData: task })` for each task element.
- Handle `onEventCreated` (Vue: `@event-created`) after a successful drop: open an anchored `Popup` targeting `args.target` with `display="anchored"`. The popup contains a read-only `Input` prefilled with `args.event.title`, a `Textarea` for details, and a `Select` for technician assignment, with an `['ok']` button to confirm. Show a `Toast` with `'New task added'` when the popup closes. Angular: use `@ViewChild` to get the `MbscPopup` instance and call `.open()` imperatively.
- Handle `onEventCreateFailed` and `onEventUpdateFailed` (Vue: `@event-create-failed`, `@event-update-failed`) to show a `Toast` when a drop lands on a weekend or other invalid date.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-vue.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config vue
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config vue --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/vue/getting-started/installation

## Related demos

- Previous: [Double Booking Prevention](https://demo.mobiscroll.com/vue/eventcalendar/prevent-double-booking-events.md) - Prevent overlapping events in a Vue event calendar — block double bookings per event or across all events with in-calendar conflict alerts.
- Next: [Drag and Drop from External Lists](https://demo.mobiscroll.com/vue/eventcalendar/external-drag-drop-sortable-dragula.md) - Drag items from external SortableJS or Dragula lists onto a Vue event calendar to schedule events — or drag them out to unschedule.
