I'm using the FullCalendar vue3 component with Laravel Breeze/Vue, but I haven't been able to get the calendar to refresh when needed. I'd like the calendar to refresh after a select element changes to show a new calendar for a different user.
I'm using the Vue3 composition api, but I can't see how to force the calendar to refresh. I think I need to use the Calendar api, but it doesn't seem to work.
Here's a scaled down version of the code:
<script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import { reactive, ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
import { Head, Link, useForm, router } from '@inertiajs/vue3';
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
const props = defineProps({
'calendar_users': Object,
});
const calendarFor = reactive({
id: -1,
});
const calendarOptions = reactive({
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin,
],
initialView: 'dayGridMonth',
headerToolbar: {
start: 'prevYear,nextYear prev,next today',
center: 'title',
end: 'dayGridMonth,timeGridWeek,timeGridDay'
},
events: '/get_events' + '?user=' + calendarFor.id,
height: 'auto',
});
function refresh_calendar() {
let calApi = this.$refs.fullCalendar.getApi();
calApi.refetchEvents();
}
</script>
<template>
<Head title="Calendar" />
<AuthenticatedLayout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800">
Office Calendar
</h2>
</template>
<div class="py-3">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden sm:rounded-lg" id="CalendarScreen" name="CalendarScreen">
<div class="flex">
<div v-if="true" id="left_side" class="min-w-48 m-1 border">
<div class="flex mt-8">
<label for="calendarFor" class="ml-3 mr-4 font-bold text-lg">Calendar for:</label>
<select v-model="calendarFor.id" class=" border rounded-md" id="calendarFor" @change="refresh_calendar()">
<option value="-1">****</option>
<option v-for="calendar_user in props.calendar_users" :key="calendar_user.id" :value="calendar_user.id">
{{ calendar_user.initials }}
</option>
</select>
</div>
</div>
<div id="right_side" class="">
<div class="p-4 text-gray-900">
<FullCalendar ref='fullCalendar' :options="calendarOptions" />
</div>
</div>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
</template>
When the page loads the events are loaded and there are no errors. But the following error occurs when I call the refresh_calendar() function when changing the calendar user:
Also, here's some info from the Vue Devtools showing the ref to the calendar component:
What am I doing wrong? How can I get the fullcalendar vue3 component to refresh based upon the selected user?
Thanks
In case someone is having difficulty getting the Fullcalendar api functions to work with the Fullcalendar vue component in the composition api, I figured I'd post this.
The answer was explained in a comment above, referencing a possible duplicate question. Because the script setup runs before the DOM is loaded, to get things working you need to put the call to getApi() in the onMounted function.
Here's a basic outline of what I did:
<script setup>
import { reactive, ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
const fullCalendar = ref(null);
let calApi = null;
const calendarOptions = reactive({
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin,
],
...
});
onMounted(() => {
calApi = fullCalendar.value.getApi();
});
</script>
<template>
<FullCalendar ref='fullCalendar' :options="calendarOptions" />
</template>
Then, if you need to do something beyond what's available in calendarOptions, you can call any fullcalendar function using the calApi variable, for example, calApi.refetchEvents();
Thanks to Estus Flask above for helping sort this out!