Search code examples
javascriptvue.jsfullcalendarlaravel-vue

How can I get the FullCalendar vue3 component to refresh?


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:

Screenshot of console error message.

Also, here's some info from the Vue Devtools showing the ref to the calendar component: Screenshot of devtools showing ref.

What am I doing wrong? How can I get the fullcalendar vue3 component to refresh based upon the selected user?

Thanks


Solution

  • 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!