I am attempting to enable inputted events on the following Vuetify calendar:
https://github.com/vuetifyjs/vuetify/blob/master/packages/docs/src/examples/calendars/complex/events.vue.
I have set up my calendar to accept inputted data submitted in a form, including name, details, start, end, color. When I submit the form, an error occurs saying that
the start and end properties are required on all events to be a valid timestamp in the format YYYY-MM-DD.
I am using type="date" in the start and end input fields, which render the dates in the MM-DD-YYYY. I tried using the Vuetify date picker instead in order to get the fields to render in YYYY-MM-DD format, but to no avail. How can I reconfigure this calendar to instead accept MM-DD-YYYY format for dates?
CALENDAR MODIFIED:
moment added to function
async addEvent () {
this.start = await new Date(this.start).toISOString().substring(0,10)
this.end = await new Date(this.end).toISOString().substring(0,10)
this.events.push({name: this.name})
this.events.push({details: this.details})
this.events.push({start: this.start})
this.events.push({end: this.end})
this.events.push({color: this.color})
},
Full Code
<template>
<v-row class="fill-height">
<v-col>
<v-sheet height="64">
<v-toolbar flat color="white">
<v-btn outlined class="mr-4" @click="setToday">
Today
</v-btn>
<v-btn fab text small @click="prev">
<v-icon small>mdi-chevron-left</v-icon>
</v-btn>
<v-btn fab text small @click="next">
<v-icon small>mdi-chevron-right</v-icon>
</v-btn>
<v-btn
color="primary"
dark
@click.stop="dialog = true"
>
New Event
</v-btn>
<v-toolbar-title>{{ title }}</v-toolbar-title>
<div class="flex-grow-1"></div>
</v-toolbar>
</v-sheet>
<v-dialog v-model="dialog" max-width="500">
<v-card>
<v-container>
<v-form @submit.prevent="addEvent">
<v-text-field v-model="name" type="text" label="name"></v-text-field>
<v-text-field v-model="details" type="text" label="detail"></v-text-field>
<v-text-field v-model="start" type="date" label="start"></v-text-field>
<v-text-field v-model="end" type="date" label="end"></v-text-field>
<v-text-field v-model="color" label="color"></v-text-field>
<v-btn type="submit" color="success" class="mr-4" @click.stop="dialog = false">
submit
</v-btn>
</v-form>
</v-container>
</v-card>
</v-dialog>
<v-sheet height="600">
<v-calendar
ref="calendar"
v-model="focus"
color="primary"
:events="events"
:event-color="getEventColor"
:event-margin-bottom="3"
:now="today"
:type="type"
@click:event="showEvent"
@click:more="viewDay"
@click:date="viewDay"
@change="updateRange"
></v-calendar>
<v-menu
v-model="selectedOpen"
:close-on-content-click="false"
:activator="selectedElement"
full-width
offset-x
>
<v-card
color="grey lighten-4"
min-width="350px"
flat
>
<v-toolbar
:color="selectedEvent.color"
dark
>
<v-btn icon>
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-toolbar-title v-html="selectedEvent.name"></v-toolbar-title>
<div class="flex-grow-1"></div>
<v-btn icon>
<v-icon>mdi-heart</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<span v-html="selectedEvent.details"></span>
</v-card-text>
<v-card-actions>
<v-btn
text
color="secondary"
@click="selectedOpen = false"
>
Cancel
</v-btn>
</v-card-actions>
</v-card>
</v-menu>
</v-sheet>
</v-col>
</v-row>
</template>
<script>
//import moment from 'moment'
export default {
data: () => ({
today: '2019-01-08',
focus: '2019-01-08',
type: 'month',
typeToLabel: {
month: 'Month',
week: 'Week',
day: 'Day',
'4day': '4 Days',
},
name: null,
details: null,
start: null,
end: null,
color: null,
selectedEvent: {},
selectedElement: null,
selectedOpen: false,
events: [
{
name: 'Vacation',
details: 'Going to the beach!',
start: '2018-12-29',
end: '2019-01-01',
color: 'blue',
}
],
dialog: false,
}),
computed: {
title () {
const { start, end } = this
if (!start || !end) {
return ''
}
const startMonth = this.monthFormatter(start)
const endMonth = this.monthFormatter(end)
const suffixMonth = startMonth === endMonth ? '' : endMonth
const startYear = start.year
const endYear = end.year
const suffixYear = startYear === endYear ? '' : endYear
const startDay = start.day + this.nth(start.day)
const endDay = end.day + this.nth(end.day)
switch (this.type) {
case 'month':
return `${startMonth} ${startYear}`
case 'week':
case '4day':
return `${startMonth} ${startDay} ${startYear} - ${suffixMonth} ${endDay} ${suffixYear}`
case 'day':
return `${startMonth} ${startDay} ${startYear}`
}
return ''
},
monthFormatter () {
return this.$refs.calendar.getFormatter({
timeZone: 'UTC', month: 'long',
})
},
},
methods: {
viewDay ({ date }) {
this.focus = date
this.type = 'day'
},
getEventColor (event) {
return event.color
},
setToday () {
this.focus = this.today
},
prev () {
this.$refs.calendar.prev()
},
next () {
this.$refs.calendar.next()
},
async addEvent () {
this.start = await new Date(this.start).toISOString().substring(0,10)
this.end = await new Date(this.end).toISOString().substring(0,10)
console.log(this.start)
this.events.push({name: this.name})
this.events.push({details: this.details})
this.events.push({start: this.start})
this.events.push({end: this.end})
this.events.push({color: this.color})
},
showEvent ({ nativeEvent, event }) {
const open = () => {
this.selectedEvent = event
this.selectedElement = nativeEvent.target
setTimeout(() => this.selectedOpen = true, 10)
}
if (this.selectedOpen) {
this.selectedOpen = false
setTimeout(open, 10)
} else {
open()
}
nativeEvent.stopPropagation()
},
updateRange ({ start, end }) {
// You could load events from an outside source (like database) now that we have the start and end dates on the calendar
this.start = start
this.end = end
},
nth (d) {
return d > 3 && d < 21
? 'th'
: ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'][d % 10]
},
},
}
</script>
As the developer of the Vuetify calendar, here's my two cents. The reason why events take a string is to distinguish between an all-day event and an event that is timed. An all-day event is in the format of YYYY-MM-DD and a timed event has the date format YYYY-MM-DD HH:mm:ss (where seconds is optional, and numbers don't need to be zero padded.)
Having said that, when this PR is merged in and released you will be able to pass dates or UTC timestamps:
https://github.com/vuetifyjs/vuetify/pull/11198
You can then define a function or property on the event that marks it as a timed event.
No need to moment.js
which will be used only for 2 lines of code, so you could do it simply like :
addEvent () {
this.start = new Date(this.start).toISOString().substring(0,10);
this.end = new Date(this.end).toISOString().substring(0,10);
...
this.events.push({name: this.name,
details: this.details,
start: this.start,
end: this.end,
color: this.color})
See the Pen
Vuetify Example Pen by boussadjra (@boussadjra)
on CodePen.
I would advice to format your date to MM-DD-YYYY
instead of modifying vuetify, then convert it to whatever format you need when you submit the form.
Moment.js is an awesome library for this type of conversions and has an easy API.
In your case, you could simply do this.end = moment(this.end).format('MM-DD-YYYY');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With