[英]Saving and updating event data with a Livewire modal in Laravel 9 FullCalendar project
我設法構建了一個 Laravel 9 項目,其中 FullCalendar 5 作為 Livewire 組件。 我有一個構造的單一模態,當單擊日歷上的開放日期(觸發“選擇”功能)和單擊現有事件(觸發 eventClick 函數)時,它會被觸發。 eventClick function 在模態的輸入字段中填充來自數據庫的事件數據。
我需要幫助獲取模式中 updateEvent 和 addEvent 函數上的“保存”按鈕,以分別在數據庫中創建或編輯信息。
這是 fullCalendar 刀片格式的完整視圖:
<style>
#calendar-container {
display: grid;
grid-template-columns: 200px 1fr;
padding: 20px;
}
#events {
grid-column: 1;
}
#calendar {
grid-column: 2;
height: 700px;
}
.dropEvent {
background-color: DodgerBlue;
color: white;
padding: 5px 16px;
margin-bottom: 10px;
text-align: center;
display: inline-block;
font-size: 16px;
border-radius: 4px;
cursor:pointer;
}
</style>
<div>
@include('livewire.eventmodal')
<div>
<!-- sidebar -->
<div id="calendar-container" wire:ignore>
<div id="events">
<div data-event='{"title":"Evénement A"}' class="dropEvent">Event One Drag</div>
<div data-event='{"title":"Evénement B"}' class="dropEvent">Event Two Draggable</div>
</div>
<div id="calendar"></div>
</div>
</div>
</div>
@push('scripts')
<script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.10.1/main.min.js'></script>
<script>
create_UUID = () => {
let dt = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
let r = (dt + Math.random() * 16) % 16 | 0;
dt = Math.floor(dt / 16);
return (c == 'x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
}
document.addEventListener('livewire:load', function () {
const Calendar = FullCalendar.Calendar;
const calendarEl = document.getElementById('calendar');
const Draggable = FullCalendar.Draggable;
new Draggable(document.getElementById('events'), {
itemSelector: '.dropEvent'
});
const calendar = new Calendar(calendarEl, {
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
views: {
resourceMonth: {
type: 'resourceTimelineMonth',
buttonText: 'personnel'
}
},
customButtons: {
promptResource: {
text: "+ personnel",
click: function() {
var title = prompt("Name");
if (title) {
calendar.addResource({
title: title
});
fetch("add_resources.php", {
method: "POST",
headers: {
Accept: "application/json"
},
body: encodeFormData({ title: title })
})
.then(response => console.log(response))
.catch(error => console.log(error));
}
}
}
},
initialView: 'resourceTimelineMonth',
locale: '{{ config('app.locale') }}',
events: JSON.parse(@this.events),
resourceAreaHeaderContent: 'Personnel',
resources: JSON.parse(@this.resources),
//'https://fullcalendar.io/api/demo-feeds/resources.json?with-nesting&with-colors',
editable: true,
eventResize: info => @this.eventChange(info.event),
eventDrop: info => @this.eventChange(info.event),
eventReceive: info => {
const id = create_UUID();
info.event.setProp('id', id);
@this.eventAdd(info.event);
},
selectable: true,
select: function(info) {
$('#eventModal').modal('show');
},
eventClick: function(info, event) {
// Display the modal and set the values to the event values.
$('#updateEventModal').modal('show');
$('#updateEventModal').find('#title').val(info.event.title);
$('#updateEventModal').find('#acronym').val(info.event.extendedProps.acronym);
$('#updateEventModal').find('#city').val(info.event.extendedProps.city);
$('#updateEventModal').find('#venue').val(info.event.extendedProps.venue);
$('#updateEventModal').find('#value').val(info.event.extendedProps.value);
$('#updateEventModal').find('#start').val(info.event.start);
$('#updateEventModal').find('#end').val(info.event.end);
$('#calendar').calendar ('updateEvent', event);
},
});
calendar.render();
});
</script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.css' rel='stylesheet' />
@endpush
這是日歷組件:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Event;
use App\Models\Resource;
use Illuminate\Support\Arr;
class Calendar extends Component
{
public $events, $resources, $title, $acronym, $city, $venue, $value ;
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 saveEvent()
{
$validatedData = $this->validate();
Event::create($validatedData);
session()->flash('message','Event Added Successfully');
$this->resetInput();
$this->dispatchBrowserEvent('close-modal');
}
public function render()
{
$this->events=json_encode(Event::all());
$this->resources=json_encode(Resource::all());
return view('livewire.calendar');
}
public function eventAdd ( $event )
{
Event::create ( $event );
}
public function eventRemove ( $id )
{
Event::destroy ( $id );
}
public function resourceAdd ( $resources )
{
Resource::create ( $resources );
}
public function resourceRemove ( $id )
{
Resource::destroy ( $id );
}
}
我設法構建了 2 個與 livewire 組件內聯的模態。 一種用於添加新事件,另一種用於編輯現有事件。 兩個問題:
這是我的觀點:
<style>
#calendar-container {
display: grid;
grid-template-columns: 200px 1fr;
padding: 20px;
}
#events {
grid-column: 1;
}
#calendar {
grid-column: 2;
height: 700px;
}
.dropEvent {
background-color: DodgerBlue;
color: white;
padding: 5px 16px;
margin-bottom: 10px;
text-align: center;
display: inline-block;
font-size: 16px;
border-radius: 4px;
cursor:pointer;
}
</style>
<div>
@include('livewire.eventmodal')
<div>
<!-- sidebar -->
<div id="calendar-container" wire:ignore>
<div id="events">
<div data-event='{"title":"Evénement A"}' class="dropEvent">Event One Drag</div>
<div data-event='{"title":"Evénement B"}' class="dropEvent">Event Two Draggable</div>
</div>
<div id="calendar"></div>
</div>
</div>
<!-- start add event modal -->
<div id="AddEventModal" class="modal fade">
<form action="/events" method="POST">
{{csrf_field()}}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4>Add Event</h4>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span> <span class="sr-only">close</span></button>
</div>
<!-- <form wire:submit.prevent="saveEvent"> -->
<div id="modalBody" class="modal-body">
<div class="bg-white sm:p-6">
<label for="title" class="block font-medium text-sm text-gray-700">Title</label>
<input type="text" name="title" id="title" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('title', '') }}" />
@error('title')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="acronym" class="block font-medium text-sm text-gray-700">Acronym</label>
<input type="text" name="acronym" id="acronym" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('acronym', '') }}" />
@error('acronym')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="city" class="block font-medium text-sm text-gray-700">City</label>
<input type="text" name="city" id="city" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('city', '') }}" />
@error('city')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="venue" class="block font-medium text-sm text-gray-700">Venue</label>
<input type="text" name="venue" id="venue" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('venue', '') }}" />
@error('venue')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="value" class="block font-medium text-sm text-gray-700">Value</label>
<input type="text" name="value" id="value" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('value', '') }}" />
@error('value')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="start" class="block font-medium text-sm text-gray-700">Start Date</label>
<input type="text" name="start" id="start" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('start', '') }}" />
@error('start')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="end" class="block font-medium text-sm text-gray-700">End Date</label>
<input type="text" name="end" id="end" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('end', '') }}" />
@error('end')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="submit" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#AddEventModal" id="submit">Save</button>
</div>
</form>
</div>
</div>
</div>
<!--end add event modal -->
<!--start edit event modal -->
<div id="EditEventModal" class="modal fade" role="dialog">
<form action="/events" method="POST">
{{csrf_field()}}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4>Add Event</h4>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span> <span class="sr-only">close</span></button>
</div>
<!-- <form wire:submit.prevent="saveEvent"> -->
<div id="modalBody" class="modal-body">
<div class="bg-white sm:p-6">
<label for="title" class="block font-medium text-sm text-gray-700">Title</label>
<input type="text" name="title" id="title" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('title', '') }}" />
@error('title')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="acronym" class="block font-medium text-sm text-gray-700">Acronym</label>
<input type="text" name="acronym" id="acronym" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('acronym', '') }}" />
@error('acronym')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="city" class="block font-medium text-sm text-gray-700">City</label>
<input type="text" name="city" id="city" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('city', '') }}" />
@error('city')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="venue" class="block font-medium text-sm text-gray-700">Venue</label>
<input type="text" name="venue" id="venue" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('venue', '') }}" />
@error('venue')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="value" class="block font-medium text-sm text-gray-700">Value</label>
<input type="text" name="value" id="value" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('value', '') }}" />
@error('value')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="start" class="block font-medium text-sm text-gray-700">Start Date</label>
<input type="text" name="start" id="start" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('start', '') }}" />
@error('start')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="bg-white sm:p-6">
<label for="end" class="block font-medium text-sm text-gray-700">End Date</label>
<input type="text" name="end" id="end" type="text" class="form-input rounded-md shadow-sm mt-1 block w-full"
value="{{ old('end', '') }}" />
@error('end')
<p class="text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
<button type="submit" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#EditEventModal" id="updateBtn">Save</button>
</div>
</form>
</div>
</div>
</div>
@push('scripts')
<script src='https://cdn.jsdelivr.net/npm/moment@2.27.0/min/moment.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/@fullcalendar/moment@5.11.3/main.global.min.js'></script>
<script>
create_UUID = () => {
let dt = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
let r = (dt + Math.random() * 16) % 16 | 0;
dt = Math.floor(dt / 16);
return (c == 'x' ? r :(r&0x3|0x8)).toString(16);
});
return uuid;
}
document.addEventListener('livewire:load', function () {
const Calendar = FullCalendar.Calendar;
const calendarEl = document.getElementById('calendar');
const Draggable = FullCalendar.Draggable;
new Draggable(document.getElementById('events'), {
itemSelector: '.dropEvent'
});
const calendar = new Calendar(calendarEl, {
titleFormat: function(date) {
return date.toString() + '!!!';
},
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
views: {
resourceMonth: {
type: 'resourceTimelineMonth',
buttonText: 'personnel'
}
},
customButtons: {
promptResource: {
text: "+ personnel",
click: function() {
var title = prompt("Name");
if (title) {
calendar.addResource({
title: title
});
fetch("add_resources.php", {
method: "POST",
headers: {
Accept: "application/json"
},
body: encodeFormData({ title: title })
})
.then(response => console.log(response))
.catch(error => console.log(error));
}
}
}
},
initialView: 'resourceTimelineMonth',
locale: '{{ config('app.locale') }}',
events: JSON.parse(@this.events),
resourceAreaHeaderContent: 'Personnel',
resources: JSON.parse(@this.resources),
//'https://fullcalendar.io/api/demo-feeds/resources.json?with-nesting&with-colors',
editable: true,
//eventLimit: true,
eventResize: info => @this.eventChange(info.event),
eventDrop: info => @this.eventChange(info.event),
eventReceive: info => {
const id = create_UUID();
info.event.setProp('id', id);
@this.eventAdd(info.event);
},
selectable: true,
select: function(selectionInfo) {
$('#AddEventModal').find('#start').val(selectionInfo.start);
$('#AddEventModal').find('#end').val(selectionInfo.end);
//alert('selected ' + info.startStr + ' to ' + info.endStr);
$('#AddEventModal').modal('show');
$('#saveBtn').click(function() {
var title = $('#title').val();
var start = moment(start).format('YYYY-MM-DD');
var end = moment(end).format('YYYY-MM-DD');
$.ajax({
url:"{{ route('event.store') }}",
type:"POST",
dataType:'json',
data:{ title, acronym, city, venue, value, start, end, color },
success:function(response)
{
$('#AddEventModal').modal('hide')
$('#calendar').fullCalendar('renderEvent', {
'title': response.title,
'acronym': response.acronym,
'city': response.city,
'venue': response.venue,
'value': response.value,
'start' : response.start,
'end' : response.end,
'color' : response.color
});
},
error:function(error)
{
if(error.responseJSON.errors) {
$('#titleError').html(error.responseJSON.errors.title);
}
},
});
});
},
eventClick: function(info) {
// var start = moment(event.start).format('YYYY-MM-DD');
// var end = moment(event.end).format('YYYY-MM-DD');
$('#EditEventModal').modal('show');
$('#EditEventModal').find('#id').val(info.event.id);
$('#EditEventModal').find('#title').val(info.event.title);
$('#EditEventModal').find('#acronym').val(info.event.extendedProps.acronym);
$('#EditEventModal').find('#city').val(info.event.extendedProps.city);
$('#EditEventModal').find('#venue').val(info.event.extendedProps.venue);
$('#EditEventModal').find('#value').val(info.event.extendedProps.value);
$('#EditEventModal').find('#start').val(info.event.start);
$('#EditEventModal').find('#end').val(info.event.end);
$('#EditEventModal').modal('show');
$.ajax({
url:"{{ route('calendar.update', '') }}" +'/'+ id,
type:"PATCH",
dataType:'json',
data:{ start, end },
success:function(response)
{
swal("Good job!", "Event Updated!", "success");
},
error:function(error)
{
console.log(error)
},
});
},
});
calendar.render();
$("#AddEventModal").on("hidden.bs.modal", function () {
$('#saveBtn').unbind();
});
$("#EditEventModal").on("hidden.bs.modal", function () {
$('#updateBtn').unbind();
});
$('.fc-event').css('font-size', '13px');
$('.fc-event').css('width', '20px');
$('.fc-event').css('border-radius', '50%');
});
</script>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@5.11.3/main.min.css' rel='stylesheet' />
@endpush
這是我的活動 controller:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreEventRequest;
use App\Http\Requests\UpdateEventRequest;
use App\Models\Event;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Symfony\Component\HttpFoundation\Response;
class EventsController extends Controller
{
public function index()
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$events = Event::all();
return view('events.index', compact('events'));
}
public function create()
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return view('events.create');
}
public function store(Request $request)
{
$request->validate([
'title' => 'required|string'
]);
$event = Event::create([
'title' => $request->title,
'acronym' => $request->acronym,
'city' => $request->city,
'venue' => $request->venue,
'value' => $request->value,
'start' => $request->start,
'end' => $request->end,
]);
$color = null;
if($event->title == 'Test') {
$color = '#924ACE';
}
return redirect('/dashboard')-> with('success', 'Event Added'); response()->json([
'id' => $event->id,
'title' => $event->title,
'acronym' => $request->acronym,
'city' => $request->city,
'venue' => $request->venue,
'value' => $request->value,
'start' => $event->start,
'end' => $event->end,
// 'color' => $color ? $color: '',
]);
}
public function show(Event $event)
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return view('events.show', compact('event'));
}
public function edit(Event $event)
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
return view('events.edit', compact('event'));
}
public function update(Request $request ,$id)
{
$event = Event::find($id);
if(! $event) {
return response()->json([
'error' => 'Unable to locate the event'
], 404);
}
$event->update([
'start' => $request->start,
'end' => $request->end,
]);
return response()->json('Event updated');
}
public function destroy(Event $event)
{
abort_if(Gate::denies('event_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
$event->delete();
return redirect()->route('events.index');
}
}
這是相關的路線:
Route::get('calendar/index', [CalendarController::class, 'index'])->name('calendar.index');
Route::post('calendar', [EventController::class, 'store'])->name('event.store');
Route::patch('calendar/update/{id}', [CalendarController::class, 'update'])->name('calendar.update');
Route::delete('calendar/destroy/{id}', [CalendarController::class, 'destroy'])->name('calendar.destroy');
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.