Petitemo
Petitemo

Reputation: 275

Problems on events update and creation on fullcalendar (rendering events infos and multiple events)

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

Answers (1)

Petitemo
Petitemo

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

Related Questions