Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

FullCalendar doesn't refresh after events update

Hello,

In an app with Laravel, VueJS 3, InertiaJS and FullCalendar, I set the events as a props array passed to the view.

<full-calendar :options="calendarOptions"></full-calendar>
...
import '@fullcalendar/core/vdom'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import listPlugin from '@fullcalendar/list'
import interactionPlugin from '@fullcalendar/interaction'
...
const calendarOptions = reactive({
  events: props.events,
  ...
})

For the moment, I have also the events displayed in a simple datatable beside the calendar. When I add, update or remove an event from the datatable, the events are updated, the datatable is updated, but the calendar doesn't refresh.

I have added {{ events }} in the view just above the calendar to check to be sure that the events array is updated, and that's the case, the events array is updated as expected.

So when I handle the events, the datatable is updated, the events array is updated, but not the calendar.

I have tried several things to solve this problem : eventsRefresh, ... from FullCalendar, but nothing works.

Do you have any idea why the calendar doesn't refresh ?

What should I check to find the solution ?

Thanks for your help.

Vincent

0 likes
8 replies
MohamedTammam's avatar

Does that value calendarOptions refresh?

And how are you setting your options?

1 like
vincent15000's avatar

@MohamedTammam I have tried to reassign the events after having updated an event.

calendarOptions.events = props.events

But the calendar doesn't refresh.

I think it's probably a problem with rendering, but I'm not sure.

vincent15000's avatar

@MohamedTammam

<template>

  <el-tab-pane label="Cours" name="courses">
		<el-card>
	    <template #header>
	    	<el-row align="middle" justify="space-between">
		      <div>Liste des cours</div>
	        <el-button type="primary" @click="create()">Ajouter un nouveau cours</el-button>
				</el-row>
	    </template>
	    <el-table
	    	ref="table"
	      :data="courses"
		    :default-sort = "{prop: 'start_date', order: 'ascending'}"
		    style="width: 100%"
		    table-layout="auto">
		    <el-table-column
		    	prop="topic.unit.name"
		      label="UE">
		    </el-table-column>
		    <el-table-column
		    	prop="topic.name"
		      label="Thème">
		    </el-table-column>
		    <el-table-column
		    	prop="date_fr"
		      label="Date">
		    </el-table-column>
		    <el-table-column
		    	prop="time_range_fr"
		      label="Horaire">
		    </el-table-column>
		    <el-table-column
		    	prop="hours_number"
		      label="Durée (h)">
		    </el-table-column>
		    <el-table-column
		    	prop="room.name"
		      label="Salle">
		    </el-table-column>
		    <el-table-column
		      label="Action"
		      align="right">
		      <template #default="scope">
			      <el-button type="primary" @click="edit(scope.row.id)">Editer</el-button>
			      <el-button type="danger" @click="destroy(scope.row.id)">Supprimer</el-button>
			    </template>
		    </el-table-column>
	    </el-table>
		</el-card>

	</el-tab-pane>

  <el-tab-pane id="calendar-wrapper" label="Calendrier" name="calendar">
  	{{events}}
  	<full-calendar :options="calendarOptions"></full-calendar>
  </el-tab-pane>

	<course-form
		:dialogVisible="dialogVisible"
		:courseId="courseId"
		:trainingId="trainingId"
		:startDate="startDate"
		:hoursNumber="hoursNumber"
		@submit="submit()"
		@cancel="cancel()"
	>
	</course-form>

</template>

<script setup>

	import { ref, reactive, computed, onMounted } from 'vue'
	import { Inertia } from '@inertiajs/inertia'
	import { Link } from '@inertiajs/inertia-vue3'
	import CourseForm from '@/Pages/User/Courses/Form.vue'

	import '@fullcalendar/core/vdom'
	import FullCalendar from '@fullcalendar/vue3'
	import dayGridPlugin from '@fullcalendar/daygrid'
	import timeGridPlugin from '@fullcalendar/timegrid'
	import listPlugin from '@fullcalendar/list'
	import interactionPlugin from '@fullcalendar/interaction'
	import dayjs from 'dayjs'

	const emit = defineEmits(['submit'])

	const props = defineProps({
		trainingId: Number,
		courses: Array,
		events: Array,
	})

	const dialogVisible = ref(false)

	const courseId = ref(null)
	const startDate = ref(null)
	const hoursNumber = ref(null)

	const calendarOptions = reactive({
	  locale: 'fr',
		timeZone: 'Europe/Paris',
		weekNumberCalculation: 'ISO',
	  plugins: [ dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin ],
	  initialView: 'timeGridWeek',
	  headerToolbar: {
	      start: 'today prev,next',
	      center: 'title',
	      end: 'dayGridMonth,timeGridWeek,listWeek',
	  },
		buttonText: {
		  today: 'Aujourdhui',
		  month: 'Mois',
		  week: 'Semaine',
		  day: 'Jour',
		  list: 'Liste',
		},
		weekNumbers: true,
		weekText: 'S',
	  slotMinTime: '08:00:00',
	  slotMaxTime: '20:00:01',
	  height: 'auto',
	  hiddenDays: [0, 6],
		editable: true,
		selectable: true,
	  events: function(info, successCallback, failureCallback) {
	    successCallback(props.events)
	  },
	  // componentDidUpdate: function(nextProps, nextState) {
	  // 	FullCalendar.rerenderEvents()
	  // },
	  eventsRefresh: function(info) {
	  	console.log('events refreshed')
	  },
    eventDidMount: function(info) {
      // let room = document.createElement('div')
      // room.innerHTML = info.event.extendedProps.room
      // let trainers = document.createElement('div')
      // trainers.innerHTML = info.event.extendedProps.trainers
      // document.querySelector('.fc-event-title-container').append(room)
      // document.querySelector('.fc-event-title-container').append(trainers)
    },
	  eventClick: function(info) {
	  	edit(info.event.extendedProps.course_id)
	  },
		select: function(selectInfo) {
			create(dayjs(selectInfo.start).utc().format(), dayjs(selectInfo.end).diff(selectInfo.start, 'hour', true))
		},
	  eventChange: function(info) {
	  	const course_id = info.event.extendedProps.course_id
	  	const form = {
	  		start_date: info.event.start,
		  	hours_number: dayjs(info.event.end).diff(info.event.start, 'hour', true)
	  	}
      axios.put('/courses/'+course_id, { form }).then(response => {
  			Inertia.reload({ only: ['courses', 'events'] })
      })
	  },
	})

	onMounted(() => {
	})

  function create(start_date, hours_number) {
  	console.log(start_date)
  	startDate.value = start_date
  	hoursNumber.value = hours_number
  	dialogVisible.value = true
  }

  function edit(id) {
  	courseId.value = id
  	dialogVisible.value = true
  }

  function destroy(id) {
    axios.delete('/courses/'+id).then(response => {
  		Inertia.reload({ only: ['courses', 'events'] })
    })
  }

	function submit() {
		this.close()
		emit('submit')
		calendarOptions.events = props.events
	}

  function cancel() {
  	this.close()
  }

  function close() {
  	dialogVisible.value = false
  	courseId.value = null
  }

</script>

<style lang="scss" scoped>

	.el-card {
		&:not(:last-child) {
			margin-bottom: 20px;
		}
	}

  .card-header {
  	display: flex;
	  justify-content: space-between;
	  align-items: center;
  }

  .el-form-item {
  	margin-bottom: 0px;
  	&:last-child {
  		margin-right: 0px;
  	}
  }

</style>
MohamedTammam's avatar
Level 51

@vincent15000 It will not work that way. When you submit a form Inertia re populate the components props. Which means. that line in the submit method

calendarOptions.events = props.events

Will run before the a form get submitted

I can now think of two options,

  1. Create a method that update the calendarOptions and call it after submitting a form in the success method. https://vuejs.org/guide/essentials/template-refs.html#ref-on-component

  2. Using watch

watch(props.events., (newValue, oldValue) => {
	calendarOptions.events = newValue;
})

1 like

Please or to participate in this conversation.