Reputation: 275
I'm using fullcalendar JS (6.0.1) with livewire on a laravel application (8.75). I have a calendar with some events.
When I click on event, a modal window opens with a form filled with informations like title, description, start date and allDay on inputs. Inputs are enabled and I can update events with a submit button or delete it with another button.
Update and deletion features work.
When I click on a day, the same modal window opens with an empty form (and deletion button doesn't appear of course). I can create an event by submitting this form, it works
My problem is that after a creation or an update, the modal is closed and I return to the calendar. My new event appears (or the modifications I've made) on it (time and title) but if I click on it to view the details, the form on the modal is empty for an event creation (no title, description, start date etc) ans empty for data I've just changed for an update.It's the same if I click on any other events on the calendar (events that have been created previously). I have to refresh the page to view the details when I click on it.
When I logged info.event on eventClick() I have all correct event data even if I just changed something. And datas shows on the calendar are correct too.
So I supposed that it's due to my modal form filled feature but I can't put my finger on it.
Here is my livewire component :
<div>
<div>
<div id='calendar-container' wire:ignore>
<div id='calendar' class="z-10"></div>
</div>
</div>
@push('scripts')
<script src='https://cdn.jsdelivr.net/npm/[email protected]/main.min.js'></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/locales-all.min.js"></script>
<script>
document.addEventListener('livewire:load', function () {
const Calendar = FullCalendar.Calendar;
const calendarEl = document.getElementById('calendar');
const calendar = new Calendar(calendarEl, {
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
},
locale: '{{config('app.locale')}}',
events: JSON.parse(@this.events),
editable: true,
eventResize: info => @this.eventChange(info.event),
eventDrop: info => @this.eventChange(info.event),
selectable: true,
unselectAuto: true,
longPressDelay: 1,
eventLongPressDelay: 1,
selectLongPressDelay: 1,
select: arg => {
arg.jsEvent.preventDefault();
// Select the modal
let modal = document.getElementById('modal')
// Empty inputs
modal.querySelectorAll('input').forEach((el) => {
el.setAttribute('value', '')
})
// Unselect checkbox
modal.querySelector('input[id="allDay"]').removeAttribute('checked')
// If deletion button is present, hide it
let deleteBtn = modal.querySelector('#delete')
if(!deleteBtn.hasAttribute('hidden')) {
deleteBtn.setAttribute('hidden', true)
}
// Fill the start date with the selected date
modal.querySelector('input[id="start"]').setAttribute('value', (new Date((arg.start).toString().split('GMT')[0]+' UTC').toISOString()).substr(0,23))
// Change modal title
modal.querySelector('#modal-title').innerText = 'Create an event'
// If modal is hidden
if(modal.hasAttribute('hidden')) {
// Display it
modal.removeAttribute('hidden')
}
// On submitting form
modal.querySelector('button[id="submitCreatingEventForm"]').addEventListener('click', (evt) => {
evt.preventDefault()
// evt.stopImmediatePropagation()
if(title && start) {
// allDay definition
let allD = allDay.checked ? true : false
// Adding event on db
@this.eventAdd({
title: title.value,
description: description.value,
start: start.value,
end: end.value,
allDay: allD
});
// Adding event on calendar
calendar.addEvent({
title: title.value,
description: description.value,
start: start.value,
end: end.value,
allDay: allD
});
}
})
// @this.render()
// Close modal button
document.getElementById('close').addEventListener('click', (evt) => {
document.getElementById('modal').setAttribute('hidden', true)
@this.render()
// Unselect the selection
calendar.unselect();
})
// Unselect the selection
calendar.unselect();
calendar.render();
},
eventClick: info => {
info.jsEvent.preventDefault();
console.log(info.event);
// Select the modal
let modal = document.getElementById('modal')
// Empty inputs
modal.querySelectorAll('input').forEach((el) => {
el.setAttribute('value', '')
})
// Unselect checkbox
modal.querySelector('input[id="allDay"]').removeAttribute('checked')
let deleteBtn = modal.querySelector('#delete')
// If deletion button is hidden
if(deleteBtn.hasAttribute('hidden')) {
// Display it
deleteBtn.removeAttribute('hidden')
}
// If a report has already been create, disabled the deletion button
if(info.event.extendedProps.report) {
deleteBtn.setAttribute('disabled', '')
} else {
deleteBtn.removeAttribute('disabled')
}
// deletion button
deleteBtn.addEventListener('click', (evt) => {
evt.preventDefault()
// Remove event form db
@this.eventRemove(info.event.id)
// remove event from calendar
info.event.remove()
})
// Modify modal title
modal.querySelector('#modal-title').innerText = 'Update an event'
// If modal is hidden
if(modal.hasAttribute('hidden')) {
// Display it
modal.removeAttribute('hidden')
}
// Fill the form inputs
// Title
modal.querySelector('input[id="title"]').setAttribute('value', info.event.title)
// Description
if(info.event.extendedProps.description != null) {
modal.querySelector('input[id="description"]').setAttribute('value', info.event.extendedProps.description)
}
// Start date
modal.querySelector('input[id="start"]').setAttribute('value', (new Date((info.event.start).toString().split('GMT')[0]+' UTC').toISOString()).substr(0,23))
// allDay
if(info.event.allDay) {
modal.querySelector('input[id="allDay"]').setAttribute('checked', true)
}
// On form submitting
modal.querySelector('button[id="submitCreatingEventForm"]').addEventListener('click', (evt) => {
evt.preventDefault()
const e = info.event
if(title && start) {
// Modify calendar event object
if(title.value != e.title) {
e.setProp('title', title.value)
}
if(description.value != e.extendedProps.description) {
e.setExtendedProp( 'description', description.value )
}
if(start.value != (new Date((e.start).toString().split('GMT')[0]+' UTC').toISOString()).substr(0,23)) {
e.setStart(start.value)
}
if(allDay.checked != e.allDay) {
e.setAllDay(allDay.checked)
}
// Modify db event
@this.eventUpdate({
id: e.id,
title: title.value,
description: description.value,
start: start.value,
end: end.value,
allDay: allDay.checked
});
@this.render()
}
// deletion button
modal.querySelector('button[id="delete"]').addEventListener('click', (evt) => {
// Remove event on calendar
calendar.remove(e)
// Remove event on db
$this.eventRemove(e.id)
})
})
// close modal button
document.getElementById('close').addEventListener('click', (evt) => {
document.getElementById('modal').setAttribute('hidden', true)
calendar.render()
})
}
});
calendar.render();
});
</script>
<link href='https://cdn.jsdelivr.net/npm/[email protected]/main.min.css' rel='stylesheet' />
@endpush
<div id="modal" hidden>
<div>
<form>
<div>
<h1 id="modal-title">Créer un événement</h1>
<button id="submitCreatingEventForm" type="submit" >Enregistrer</button>
</div>
<div>
<div>
<label for="title">
Titre *
</label>
<input id="title" type="text">
</div>
</div>
<div>
<div>
<label for="description">
Description
</label>
<input id="description" type="text">
</div>
</div>
<div>
<div>
<label for="start">
Début *
</label>
<input id="start" type="datetime-local" name="start">
</div>
<div>
<label for="allDay">Journée entière</label>
<input id="allDay" type="checkbox" name="allDay" value="true">
</div>
<div hidden>
<label for="end">
Fin
</label>
<input id="end" type="datetime-local" name="end">
</div>
</div>
</form>
<div id="modal-buttons">
<button id="close">Fermer</button>
<button id="delete">Supprimer</button>
</div>
</div>
</div>
</div>
Do you see anything that could be causing this?
Here is my Calendar component :
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Event;
use App\Models\Client;
use Illuminate\Support\Arr;
class Calendar extends Component
{
public $events = [];
public function mount($events)
{
$this->events = json_encode($events);
}
public function render()
{
return view('livewire.calendar');
}
public function eventChange($event)
{
$e = Event::find($event['id']);
$e->start = $event['start'];
if(Arr::exists($event, 'end')) {
$e->end = $event['end'];
}
$e->save();
}
public function eventAdd($event)
{
Event::create($event);
}
public function eventRemove($id)
{
Event::destroy($id);
}
public function eventUpdate($event)
{
$e = Event::find($event['id']);
$e->title = $event['title'];
$e->description = $event['description'];
$e->start = $event['start'];
$e->end = $event['end'];
$e->allDay = $event['allDay'];
if($e->isDirty()) {
$e->update($event);
}
}
public function reload() {
return redirect(request()->header('Referer'));
}
}
Despite my research, I still can't find the solution.
Does anyone have a clue on it?
Upvotes: 0
Views: 481
Reputation: 275
By using removeEventListener() at each of my listeners called into select and eventClick methods and adding evt.stopImmediatePropagation() on the top of my callback functions to submit forms (for creation and update) I no longer have multiple creations.
To finish and to avoid unecessary eventlisteners called on select and eventClick methods I factorize the listener to close the modal outside the calendar object (because nothing on this function use calendar properties)
select: arg => {
[...]
let submitCreationBtn = modal.querySelector('button[id="submitCreatingEventForm"]')
submitCreationBtn.addEventListener('click', handleSubmitCreationForm)
function handleSubmitCreationForm(evt) {
evt.preventDefault()
evt.stopImmediatePropagation()
if(title && start) {
let allD = allDay.checked ? true : false
@this.eventAdd({
title: title.value,
description: description.value,
start: start.value,
end: end.value,
allDay: allD
});
calendar.addEvent({
title: title.value,
description: description.value,
start: start.value,
end: end.value,
allDay: allD
});
}
submitCreationBtn.removeEventListener('click', handleSubmitCreationForm)
}
[...]
}
eventClick: info => {
[...]
let submitUpdateBtn = modal.querySelector('button[id="submitCreatingEventForm"]')
submitUpdateBtn.addEventListener('click', handleSubmitUpdateForm)
function handleSubmitUpdateForm(evt) {
evt.preventDefault()
evt.stopImmediatePropagation()
const e = info.event
if(title && start) {
if(title.value != e.title) {
e.setProp('title', title.value)
}
if(description.value != e.extendedProps.description) {
e.setExtendedProp( 'description', description.value )
}
if(start.value != (new Date((e.start).toString().split('GMT')[0]+' UTC').toISOString()).substr(0,23)) {
e.setStart(start.value)
}
if(allDay.checked != e.allDay) {
e.setAllDay(allDay.checked)
}
@this.eventUpdate({
id: e.id,
title: title.value,
description: description.value,
start: start.value,
end: end.value,
allDay: allDay.checked
});
@this.render()
}
submitUpdateBtn.removeEventListener('click', handleSubmitUpdateForm)
}
[...]
}
<script>
[...]
let closeBtn = document.getElementById('close')
closeBtn.addEventListener('click', handleClickOnCloseBtn)
function handleClickOnCloseBtn(evt) {
document.getElementById('modal').setAttribute('hidden', true)
}
[...]
</script>
Upvotes: 1