Drag and drop events with FullCalendar - javascript

I am trying to implement a drag and drop using FullCalendar.js, the idea is basically to have a menu on the left with a list of events that are draggable and you can drop on the calendar (which would then execute a function that will add these to GoogleCalendar).
My problem is that the 'drop' is not working for some reason, I can drag the text on the top, but the "drop" event is not being fired on FullCalendar:
HTML:
<div class="wrapper wrapper-content" id="calendar-wrap">
<div class="row animated fadeInDown">
<div class="col-lg-3">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Draggable Events</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div id='external-events'>
<p>Drag a event and drop into calendar.</p>
<div id="evt1" class='external-event navy-bg'>Call Client.</div>
<div id="evt2" class='external-event navy-bg'>Confirm Client Happy with Quote.</div>
<div id="evt3" class='external-event navy-bg'>Send Quote.</div>
<div id="evt4" class='external-event navy-bg'>Something Else.</div>
<div id="evt5" class='external-event navy-bg'>One more for luck.</div>
<p class="m-t">
<input type='checkbox' id='removeEventAfterDrop' checked /> <label for='removeEventAfterDrop'>remove after drop</label>
</p>
</div>
</div>
</div>
</div>
<div class="col-lg-9">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>Calendar Monthly View </h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div id="myFullCalendar"></div>
</div>
</div>
</div>
</div>
</div>
JS:
$(document).ready(function() {
/* initialize the external events
-----------------------------------------------------------------*/
$('div.external-event').each(function() {
console.log("external event");
// store data so the calendar knows to render an event upon drop
$(this).data('event', {
title: $.trim($(this).text()), // use the element's text as the event title
stick: true // maintain when user navigates (see docs on the renderEvent method)
});
// make the event draggable using jQuery UI
$(this).draggable({
zIndex: 1000,
revert: true, // will cause the event to go back to its
revertDuration: 0 // original position after the drag
});
});
/* initialize the calendar
-----------------------------------------------------------------*/
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
$('#myFullCalendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
editable: true,
droppable: true, // this allows things to be dropped onto the calendar
drop: function(){
console.log("dropped");
},
events: [
{
title: 'All Day Event',
start: new Date(y, m, 1)
},
{
title: 'Long Event',
start: new Date(y, m, d-5),
end: new Date(y, m, d-2)
},
{
id: 999,
title: 'Repeating Event',
start: new Date(y, m, d-3, 16, 0),
allDay: false
},
]
});
});
Js Fiddle:
https://jsfiddle.net/filipecss/x74so0tz/7/
What could be causing this issue?
Thanks in advance,

It turns out the problem in my fiddle was one of the external css files:
https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.2.0/fullcalendar.print.css
Apparently fullcalender.print.css breaks the drag and drop.

Related

FullCalendar event ID - how to get it for deletion of events in with Laravel?

So, I'm working on a calendar app and I'm using Laravel and FullCalendar for this.
I want to be able to delete an event - once I click on the event in the calendar, a modal popup show up with a delete button. In order to delete the event, I must know its ID, but I can't get.
I can get it via JS selectors and display it as a text, but I can't figure out how to get it as a PHP variable so that I could use it in the action parameter of the form.
The reason why I'm doing this in the way I did it is: If I get all the events and "foreach" them into the "events" array of FullCalendar, my source code when the calendar renders would be a mess, full of events and their details. By using this approach, I find that events are not displayed in the source and the page renders more quickly (but I might be mistaken).
The way I display events from the database is as follows:
EventsController
public function getEvents()
{
return Event::where('event_date', '>', '2022-01-01')
->join('hospitals', 'events.hospital_id', '=', 'hospitals.hospital_id')
->select('hospitals.hospital_color', 'hospitals.hospital_max_people', 'event_id', 'event_content', 'event_date', 'user_id')
->get()
->map(fn ($events) => [
'id' => $events->event_id,
'title' => $events->event_content,
'start' => $events->event_date,
'allDay' => true,
'editable' => false,
'backgroundColor' => $events->hospital_color,
'borderColor' => $events->hospital_color,
'hospital_max_people' => $events->hospital_max_people,
'volunteer' => User::find($events->user_id)->first_name .' '. User::find($events->user_id)->last_name
]);
}
Events blade template - FullCalendar settings:
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
editable: false,
droppable: true,
selectable: true,
initialView: 'dayGridWeek',
views: {
dayGridWeek: {
type: 'dayGridWeek',
duration: { weeks: 2 },
buttonText: '4 day'
}
},
header: {
left: 'prev,next today',
center: 'title',
right: 'month,listMonth'
},
themeSystem: 'bootstrap5',
locale: 'en',
firstDay: '1',
contentHeight: 500,
height: 700,
expandRows: true,
events: 'getEvents',
eventClick: function(calendar, jsEvent, view) {
$('#eventDetail').modal("show");
$('#id').html(calendar.event.id);
$('#title').html(calendar.event.title);
$('#start').html(moment(calendar.event.start).format('DD.MM.YYYY'));
$('#volunteer').html(calendar.event.extendedProps.volunteer);
}
});
calendar.render();
});
</script>
Events blade template for showing modal:
<div class="modal fade" id="eventDetail" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="bar" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Event details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Start: <p id="start"></p>
Volunteer: <p id="volunteer"></p>
Title: <p id="title"></p>
</div>
<div class="modal-footer">
<form method="POST" action="{{ route('calendar.destroy') }}" class="needs-validation" novalidate=""></p>
#csrf
#method('DELETE')
<div class="form-group">
<x-button tabindex="3">
{{ __('Delete event') }}
</x-button>
</div>
</form>
</div>
</div>
</div>
</div>
So, I did it in anoter way; just wanted to post the answer - I added a hidden input field: <input type="hidden" name="id" id="id" value=""> and the value is filled out when a modal opens with addition of this to the original id selector: $('#id').html(calendar.event.id).attr('value', calendar.event.id);
I also had to alter my delete route: Route::DELETE('calendar/destroy/{event_id}'
And that's it. Thank you anyway for your help

jQuery-ui weird behavior with sortable after an element is dropped

I'm building a system that would allow a user to drag and drop elements from a dragzone to a dropzone. Once dropped, the elements are cloned "back" to their origin. Also, the user can sort the elements dropped as he wants.
I had a first issue where I couldn't clone the block I dragged, but I could sort it when it was dropped. Now that I fixed the clone problem, if I try to sort the elements, the elements moving come from the dragzone and I can't understand why.
Here is the HTML:
<div class="container-fluid">
<div class="row">
<div class="card col-3">
<div class="card-body">
<div class="card draggable-element" data-target="textarea">
<div class="card-body">
This is some text within a card body. 1
</div>
</div>
<div class="card draggable-element" data-target="textfield">
<div class="card-body">
This is some text within a card body. 2
</div>
</div>
<div class="card draggable-element" data-target="fileinput">
<div class="card-body">
This is some text within a card body. 3
</div>
</div>
</div>
</div>
<div class="card col-6 offset-1">
<div class="card-body dropzone">
</div>
</div>
</div>
</div><!-- /.container -->
And here is the JS:
$(document).ready(function() {
$('.draggable-element').draggable({
revert: 'invalid',
appendTo: '.dropzone',
helper: 'clone'
});
$('.dropzone').droppable({
drop: function (event, ui) {
// With the following code, the elements won't get cloned
var item = $(ui.draggable);
if(!item.hasClass('copy')) {
item.clone().addClass('copy');
item.draggable({
revert: 'invalid',
stack: ".draggable",
helper: 'clone'
})
}
else {
$(this).append($(ui.helper).clone());
}
$(this).append(item);
}
})
.sortable();
});
/*
drop: function (event, ui) {
// With the following code, the elements getting sorted are
// the div.draggable-element from the div.card.col-3
$(ui.draggable).clone(true).detach().css({
position: 'relative',
top: 'auto',
left: 'auto'
}).appendTo(this);
}
*/
I haven't used jquery-ui for a while so I can't find what may be obvious, I tried to mix the code together but it didn't end up well at all.
Thank you in advance
OK, this is likely NOT a full answer ( but the markup had an odd "card-body" holder of cards so I renamed that to test. Does Not "clone" as was represented in the question...so it sorts in my example but not sure this totally reproduces/resolves here. I updated the "clone" part but not sure it is what you desire.
$(document).ready(function() {
$('.draggable-element').draggable({
revert: 'invalid',
appendTo: '.dropzone',
helper: 'clone'
});
$('.dropzone').droppable({
drop: function(event, ui) {
// With the following code, the elements won't get cloned
var item = $(ui.draggable);
// hide to not obscure console.log(item.length);
if (!item.hasClass('copy')) {
var newy = item.clone(false);
newy.addClass('copy');
//console.log(newy);
newy.draggable({
revert: 'invalid',
stack: ".draggable",
helper: 'clone'
});
} else {
$(this).append(item);
}
$('.dropzone').append(newy);
// $(this).append(item);
}
})
.sortable();
});
.cards,
.dropzone {
border: solid red 1px;
height: 5em;
}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="container-fluid">
<div class="row">
<div class="card col-3">
<div class="cards">
<div class="card draggable-element" data-target="textarea">
<div class="card-body">
This is some text within a card body. 1
</div>
</div>
<div class="card draggable-element" data-target="textfield">
<div class="card-body">
This is some text within a card body. 2
</div>
</div>
<div class="card draggable-element" data-target="fileinput">
<div class="card-body">
This is some text within a card body. 3
</div>
</div>
</div>
</div>
<div class="card col-6 offset-1">
<div class="card-body dropzone">
</div>
</div>
</div>
</div>

Failled to execute 'insertBefore' on 'Node' MeteorJS

I am using the official semantic ui package for my Meteor web app and getting this error whenever I try and navigate through the vertical menu. This is causing my flowrouter routes to act wonky and not display. Thus, killing my mobile experience :(. But everything works perfect on desktop.
Error: Failed to execute 'insertBefore' on 'Node':
The node before which the new node is to be inserted is not a child of this node.
The template:
<template name="_nav">
<div>
<div class="ui grid large menu computer only">
<div class="item">
<img src="/cook.png" href="/">
</div>
<a href="/" class="item {{isActiveRoute name='home'}}">
Home
</a>
<a href="/aboutus" class="item {{isActiveRoute name='aboutus'}}">
About Us
</a>
<a href="/team" class="item {{isActiveRoute name='team'}}">
Team
</a>
<a href="/contacts" class="item {{isActiveRoute name='contacts'}}">
Contacts
</a>
<div class="ui large right menu">
{{#if isInRole 'admin'}}
<a href="/admin" class="item {{isActiveRoute name='admin'}}">
Admin
</a>
{{/if}}
<a class="ui item">
{{> loginButtons}}
</a>
</div>
</div>
<div class="ui grid secondary menu mobile tablet only">
<div class="ui container">
<a class="item toggle-menu">
<i class="big sidebar icon"></i>
</a>
<div class="ui sidebar vertical menu">
<a href="/" class="item {{isActiveRoute name='home'}}">
Home
</a>
<a href="/aboutus" class="item {{isActiveRoute name='aboutus'}}">
About Us
</a>
<a href="/team" class="item {{isActiveRoute name='team'}}">
Team
</a>
<a href="/contacts" class="item {{isActiveRoute name='contacts'}}">
Contacts
</a>
{{#if isInRole 'admin'}}
<a href="/admin" class="item {{isActiveRoute name='admin'}}">
Admin
</a>
{{/if}}
</div>
<div class="ui secondary right menu">
<a class="ui item">
{{> loginButtons}}
</a>
</div>
</div>
</div>
</div>
</template>
Events:
Template._nav.events({
'click .toggle-menu': function () {
$('.ui.sidebar')
.sidebar('toggle');
}
});
Routes (FlowRouter):
FlowRouter.route( '/' , {
action: function() {
BlazeLayout.render( '_app', {
nav: '_nav',
content: 'home',
footer: '_footer'
});
},
name: 'home'
});
FlowRouter.route( '/AboutUs' , {
action: function() {
BlazeLayout.render( '_app', {
nav: '_nav',
content: 'aboutus',
footer: '_footer'
});
},
name: 'aboutus'
});
FlowRouter.route( '/Team' , {
action: function() {
BlazeLayout.render( '_app', {
nav: '_nav',
content: 'team',
footer: '_footer'
});
},
name: 'team'
});
FlowRouter.route( '/Contacts' , {
action: function() {
BlazeLayout.render( '_app', {
nav: '_nav',
content: 'contacts_list',
footer: '_footer'
});
},
name: 'contacts'
});
FlowRouter.route( '/Admin' , {
action: function() {
BlazeLayout.render( '_app', {
nav: '_nav',
content: 'admin',
footer: '_footer'
});
},
name: 'admin'
});
CSS (if it matters):
.ui.menu .active.item {
background-color: #E0F1FF !important;
color: Black !important;
}
.ui.dropdown.item {
padding: 0;
}
.ui.dropdown.item:hover {
background-color: rgba(0, 0, 0, 0.00) !important;
}
.ui.menu {
margin: 0;
}
Again, this error only shows whenever I try and navigate with the vertical menu. I am also using the sach:db-admin package which is yogibens:admin package but for FlowRouter. Which uses twbs:bootstrap for styling. This might be causing some issues but I am unsure.
Try putting a container ( or etc) around {{#each}} or {{#if}} fields. Theres a bug with issues reported on meteor's github page. Something along the lines of replacing blocks.
https://github.com/meteor/meteor/issues/2373

Full Calendar with Modal and Multiple event details - Meteor

I'm using the FullCalendar package as well as the below code which is working perfectly:
HTML:
<body>
{{>calendar}}
</body>
</template>
<template name="calendar">
{{#if showEditEvent}}
{{>editEvent}}
{{/if}}
<div class="container">
<div class="row">
<div class="col-md-2">
</div>
<div class="col-md-8">
<div id="calendar">
</div>
</div>
<div class="col-md-2">
</div>
</div>
</div>
</template>
<template name="editEvent">
<!--Modal Dialog-->
<div class="modal" id="EditEventModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria- hidden="true">x</button>
<h4 class="modal-title">Agenda Item</h4>
</div>
<div class="modal-body">
<label for="title">Item: </label><input type="text" class="title" name="title" value="{{evt.title}}" id="title">
</div>
<div class="modal-footer">
Delete
Save
Cancel
</div>
</div>
</div>
</div>
JS:
// Set session defaults
Session.setDefault('editing_calevent', null);
Session.setDefault('showEditEvent', false);
Template.calendar.showEditEvent = function(){
return Session.get('showEditEvent');
}
Template.editEvent.evt = function(){
// run a query to the database
var calEvent = CalEvents.findOne({_id:Session.get('editing_calevent')});
return calEvent;
}
var updateCalendar = function(){
$('#calendar').fullCalendar( 'refetchEvents' );
}
Template.editEvent.events({
'click .save':function(evt,tmpl)
{updateCalEvent(Session.get('editing_calevent'),tmpl.find('.title').value);
Session.set('editing_calevent',null);
Session.set('showEditEvent',false);
},
'click .close':function(evt,tmpl){
Session.set('editing_calevent',null);
Session.set('showEditEvent',false);
$('#EditEventModal').modal("hide");
} ,
'click .remove':function(evt,tmpl){
removeCalEvent(Session.get('editing_calevent'));
Session.set('editing_calevent',null);
Session.set('showEditEvent',false);
$('#EditEventModal').modal("hide");
}
})
Template.calendar.rendered = function(){
$('#calendar').fullCalendar({
header:{
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay'
},
// Event triggered when someone clicks on a day in the calendar
dayClick:function( date, allDay, jsEvent, view) {
// Insert the day someone's clicked on
CalEvents.insert({title:'New Item',start:date,end:date});
// Refreshes the calendar
updateCalendar();
},
eventClick:function(calEvent,jsEvent,view){
// Set the editing_calevent variable to equal the calEvent.id
Session.set('editing_calevent',calEvent.id);
// Set the showEditEvent variable to true
Session.set('showEditEvent', true);
$('#EditEventModal').modal("show");
},
eventDrop:function(calEvent){
CalEvents.update(calEvent.id,
{$set: {start:calEvent.start,end:calEvent.end}});
updateCalendar();
},
events: function(start, end, callback) {
// Create an empty array to store the events
var events = [];
// Variable to pass events to the calendar
// Gets us all of the calendar events and puts them in the array
calEvents = CalEvents.find();
// Do a for each loop and add what you find to events array
calEvents.forEach(function(evt){
events.push(
{ id:evt._id,title:evt.title,start:evt.start,end:evt.end});
})
// Callback to pass events back to the calendar
callback(events);
},
editable:true
});
}
var removeCalEvent = function(id,title){
CalEvents.remove({_id:id});
updateCalendar();
}
var updateCalEvent = function(id,title){
CalEvents.update(id, {$set: {title:title}});
updateCalendar();
}
My two part question is:
A) how do i integrate something like the below in order to have more than one custom fields in the JSON feed?
B) how do I store the resultant data in a new Mongo Collection?
$(document).ready(function() {
$('#bootstrapModalFullCalendar').fullCalendar({
events: '/hackyjson/cal/',
header: {
left: '',
center: 'prev title next',
right: ''
},
eventClick: function(event, jsEvent, view) {
$('#modalTitle').html(event.title);
$('#modalBody').html(event.description);
$('#eventUrl').attr('href',event.url);
$('#fullCalModal').modal();
}
});
});
Thank you.

Meteorjs and bootstrap3 modal rare behavior

Im a newbee on Meteorjs and Im testing an implementation of FullCalendar and meteor, and I got a strange problem with this modal in particular. is a modal for edit, It is supposed to trigger when you click on a list of links of events wich I successfuly test on other apps in the pass, I already have another modal for create events on this view and works fine. but in this case, it seems to not attach the js behavior correcly. the modal is displayed, but no effects and not working the close nor the submit buttons.
I wonder if in this particular case, Im losing something or some code tag or the FullCalendar is in conflict?, and for any help Thank you.
here is the demo deply on meteor.com gbelot.todo3.meteor.com
here is the code on gitHub: https://github.com/gbelot2003/meteor-todo-bt3/blob/todo3-icalendar/client/home
and here the controller and Templates code:
Meteor.subscribe("events");
Template.body.rendered = function () {
var fc = this.$('.fc');
this.autorun(function () {
Events.find();
fc.fullCalendar('refetchEvents');
});
};
Template.calendarEdit.helpers({
events: function(){
var fc = $('.fc');
return function (start, end, tz, callback) {
//subscribe only to specified date range
Meteor.subscribe('events', start, end, function () {
//trigger event rendering when collection is downloaded
fc.fullCalendar('refetchEvents');
});
var events = Events.find().map(function (it) {
return {
title: it.date,
start: it.date,
allDay: false
};
});
callback(events);
};
},
options: function() {
return {
id: 'myid2',
class: 'myCalendars',
lang: 'es',
allDaySlot: false,
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
axisFormat: 'h:mm a',
timeFormat: {
agenda: 'h:mm a',
month: 'h:mm a',
agendaWeek: 'h:mm a'
},
firstHour: 7,
editable: true,
eventLimit: false,
events: function (start, end, timezone, callback) {
callback(Events.find({}).fetch());
},
defaultView: 'month'
};
}
});
Template.HomeTemplate.onRendered(function(){
var fc = this.$('.fc');
this.autorun(function () {
Events.find();
fc.fullCalendar('refetchEvents');
});
this.$('.datetimepicker').datetimepicker({
format: 'YYYY-MM-DD H:mm:ss'
});
});
Template.HomeTemplate.events({
'submit #new-event': function(event){
event.preventDefault();
var title = event.target.title.value;
var start = event.target.start.value;
var end = event.target.end.value;
var description = event.target.description.value;
Meteor.call("addEvent", title, start, end, description);
event.target.title.value = "";
event.target.start.value = "";
event.target.end.value = "";
event.target.description.value = "";
$("#createEvent").modal("hide");
},
/** this modal works well **/
/***************************/
'click .create': function(e){
e.preventDefault();
$("#createEvent").modal("show");
}
});
Template.HomeTemplate.helpers({
event: function(){
return Events.find();
}
});
Template.eventList.events({
'click .delete': function(event){
event.preventDefault();
id = this._id;
Meteor.call('deleteEvent', id);
},
/**** here is the call for the modal *****/
/** with problems. Did I miss something? */
/******************************************/
'click .update': function(e){
e.preventDefault();
$(".updateModal").show();
}
});
the homeTemplate here
<template name="HomeTemplate" id="home">
<div class="container">
<div class="row">
<div class="col-sm-12">
<h2>Todos & FullCalendar Test</h2>
<hr/>
</div>
</div>
<div class="row">
<div class="col-md-6 col-sm-12">
<h3>Full Calendar</h3>
<!--<button class="refresh">Refresh</button>-->
{{> calendarEdit }}
</div>
<div class="col-md-6 col-sm-12">
<h3>Events todo App <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span></h3>
<ul class="list-group">
{{#transition in="zoomIn" out="bounceOut"}}
{{#each event}}
{{> eventList}}
{{/each}}
{{/transition}}
</ul>
</div>
</div>
</div>
{{> createEvent}}
{{> updateModal}}
</template>
This is the Template where you click on link
<template name="eventList">
<li class="list-group-item">
<h4 class="list-group-item-heading">
<div class="row">
<div class="col-sm-10">
<a type="button" href="#" class="update">{{title}}</a>
</div>
<div class="col-sm-2">
<span class=" glyphicon glyphicon-trash text-danger" aria-hidden="true"></span>
</div>
</div>
</h4>
<p>{{start}} | {{end}}</p>
<p class="list-group-item-text">{{description}}</p>
</li>
</template>
this has been solve by using peppelg:bootstrap-3-modal, that package did the job!!!

Categories