I have a problem with my Angular app where I have implemented the fullcalendar. When I click on a date it selects the wrong one. It only happens on the left side and top of the date square. I attach screenshots with my pointer, the click I do it where you see the pointer.
My full calendar code:
`calendarVisible = true;
calendarOptions: CalendarOptions = {
headerToolbar: {
left: '',
center: 'title',
// right: 'refreshButton prev,next today'
right: 'refreshButton prev,next today dayGridMonth,timeGridWeek,timeGridDay,listWeek'
},
initialView: 'dayGridMonth',
contentHeight: 900,
//initialEvents: INITIAL_EVENTS, // alternatively, use the `events` setting to fetch from a feed
weekends: true,
editable: this.global.isFeatureActive(Features.updateActivity),
eventDisplay: 'block',
selectable: true,
selectMirror: true,
dayMaxEvents: false,
dayMaxEventRows: 3,
locale: itLocale,
themeSystem: 'bootstrap5',
customButtons: {
refreshButton: {
//text: 'Aggiorna',
icon: 'arrow-clockwise',
click: () => {
this.refreshEvents();
}
}
},
eventDrop: this.handleDropEvent.bind(this),
eventContent: this.customEventContentTest.bind(this),
events: (info, successCallback, failureCallback) => {
this.actService.GetCustomByRange(info.start, info.end).subscribe({
next: (res) => {
this.global.setLoad(true);
this.aeService.activitiesSub.next(res);
this.aeService.applyFilter();
//this.aeService.activitiesToEvents(res);
successCallback(this.aeService.eventsSub.getValue());
},
error: (err) => {
failureCallback(err)
},
complete: () => {
this.global.setLoad(false);
}
});
},
select: this.handleDateSelect.bind(this),
eventClick: this.handleEventClick.bind(this),
eventMouseEnter: this.handleEventMouseOver.bind(this),
eventsSet: this.handleEvents.bind(this),
eventDidMount: (info) => {
//console.log("eventDidMount",info.el);
this.customEventContent(info);
},
eventAdd: (arg) => { },
// {description: "Lecture", department: "BioChemistry"}
/* you can update a remote database when these fire:
eventAdd:
eventChange:
eventRemove:
*/
};`
Related
I am trying to render events and resources which are stored in a database. I can get the calendar to render with predefined data for events and resources, however when I push data into the array's, the new objects do not appear when the calendar is rendered.
This is the JS code I am using to obtain the data and render the calendar;
function GetCalenderDetails() {
$.ajax({
url: 'myapiendpoint',
type: 'GET',
success: function (response) {
$.each(response.bookedServices, function (i, v) {
services.push({
id: v.id,
title: v.title
});
});
console.log(JSON.stringify(services));
$.each(response.userBookings, function (i, v) {
bookings.push({
id: v.id,
resourceId: v.resourceId,
title: v.title,
start: v.start,
allDay: v.allDayFlag
});
});
console.log(JSON.stringify(bookings));
calendar.render();
},
error: function (error) {
console.log(error);
}
});
}
var bookings = new Array({ id: '1', resourceId: 'a', title: 'Meeting', start: '2021-03-14', allDay: true });
var services = new Array({ id: 'a', title: 'Room A' });
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
timeZone: 'UTC',
initialView: 'resourceTimelineDay',
aspectRatio: 1.5,
headerToolbar: {
left: 'prev,next',
center: 'title',
right: 'resourceTimelineDay,resourceTimelineWeek,resourceTimelineMonth'
},
editable: true,
resourceAreaHeaderContent: 'Resources',
resources: services,
events: bookings
});
GetCalenderDetails();
The following is the console output of the 'services' and 'bookings' variables after the GetCalenderDetails function is executed;
Resources Output
[{"id":"a","title":"Room A"},{"id":"b","title":"Room B"}]
Events Output
[{"id":"1","resourceId":"a","title":"Meeting","start":"2021-03-14","allDay":true},
{"id":"2","resourceId":"b","title":"Meeting B","start":"2021-03-15","allDay":true}]
The first resource and event will render, however the second item which is pushed into the array from the GetCalenderDetails function do not render.
I am probably missing something very obvious, but I am not seeing it, so another set of eyes might help :)
As suggested by ADyson's comments, here is the solution to my problem just in case anyone else ends up in the same position;
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
timeZone: 'UTC',
initialView: 'resourceTimelineDay',
headerToolbar: {
left: 'prev,next,today',
center: 'title',
right: 'resourceTimelineDay,resourceTimelineWeek,resourceTimelineMonth'
},
editable: true,
resourceAreaHeaderContent: 'Booked Services',
resources: function (info, successCallback, failureCallback) {
$.ajax({
url: 'myapiendpoint',
type: 'GET',
success: function (response) {
var resources = [];
console.log(response);
if (response.status == true) {
$.each(response.bookedServices, function (i, v) {
resources.push({
id: v.id,
title: v.title
});
});
} else {
//Do something
}
return successCallback(resources);
},
error: function (error) {
console.log(error);
}
});
},
events: function (info, successCallback, failureCallback) {
$.ajax({
url: 'myapiendpoint',
type: 'GET',
success: function (response) {
var events = [];
console.log(response);
if (response.status == true) {
$.each(response.userBookings, function (i, v) {
events.push({
id: v.id,
title: v.title,
start: v.start,
allDay: v.allDayFlag
});
});
} else {
//Do something
}
return successCallback(events);
},
error: function (error) {
console.log(error);
}
});
}
});
calendar.render();
I'm trying to make it possible to drag events to an external div and back into the calendar again. However, whenever I drop an event I get TypeError: jsEvent is undefined.
I'm not entirely sure why it is doing this, this should be a valid parameter that is passed to the function of eventDragStop.
I'm using the latest FullCalendar 4.
Here is my code
// Load the CSS stuff
import {Tooltip} from "bootstrap";
require('#fortawesome/fontawesome-free/css/fontawesome.min.css');
require('#fortawesome/fontawesome-free/css/brands.min.css');
require('#fortawesome/fontawesome-free/css/solid.min.css');
require('../css/app.scss');
// Load the JS stuff
let $ = require('jquery');
require('bootstrap');
require('./libs/navbar.js');
require('jquery-ui/ui/widgets/draggable');
import apiclient from "./libs/apiclient";
import { Calendar } from '#fullcalendar/core';
import dayGridPlugin from '#fullcalendar/daygrid';
import timeGridPlugin from '#fullcalendar/timegrid';
import interactionPlugin from '#fullcalendar/interaction';
import bootstrapPlugin from '#fullcalendar/bootstrap';
// $(document).ready(function () {
//
// });
document.addEventListener('DOMContentLoaded', () => {
/* initialize the external events
-----------------------------------------------------------------*/
$('#external-events .fc-event').each(function() {
// 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: 999,
revert: true, // will cause the event to go back to its
revertDuration: 0 // original position after the drag
});
});
let calendarEl = document.getElementById('calendar-holder');
let calendar = new Calendar(calendarEl, {
views: {
jira: {
type: 'dayGridWeek',
duration: {months: 3},
buttonText: 'Jira'
}
},
defaultView: 'jira',
themeSystem: 'bootstrap',
editable: true,
droppable: true,
firstDay: 1,
contentHeight: 'auto',
weekNumbersWithinDays: true,
weekNumbers: true,
eventSources: [
{
url: "/fc-load-events",
method: "POST",
extraParams: {
filters: JSON.stringify({})
},
failure: () => {
// alert("There was an error while fetching FullCalendar!");
},
},
],
header: {
left: 'prev,next today',
center: 'title',
right: 'jira,dayGridMonth,timeGridWeek',
},
plugins: [ bootstrapPlugin, interactionPlugin, dayGridPlugin, timeGridPlugin ], // https://fullcalendar.io/docs/plugin-index
timeZone: 'UTC',
eventRender: function(info) {
var tooltip = new Tooltip(info.el, {
title: info.event.title+'<br>'+info.event.extendedProps.assignee,
placement: 'top',
trigger: 'hover',
container: 'body',
html: true
});
},
dragRevertDuration: 0,
drop: function() {
// is the "remove after drop" checkbox checked?
if ($('#drop-remove').is(':checked')) {
// if so, remove the element from the "Draggable Events" list
$(this).remove();
}
},
eventDragStop: function( event, jsEvent, view ) {
if(isEventOverDiv(jsEvent.clientX, jsEvent.clientY)) {
$('#calendar-holder').calendar('removeEvents', event._id);
var el = $( "<div class='fc-event'>" ).appendTo( '#external-events-listing' ).text( event.title );
el.draggable({
zIndex: 999,
revert: true,
revertDuration: 0
});
el.data('event', { title: event.title, id :event.id, stick: true });
}
}
});
calendar.render();
let isEventOverDiv = function(x, y) {
var external_events = $( '#external-events' );
var offset = external_events.offset();
offset.right = external_events.width() + offset.left;
offset.bottom = external_events.height() + offset.top;
// Compare
if (x >= offset.left
&& y >= offset.top
&& x <= offset.right
&& y <= offset .bottom) { return true; }
return false;
}
});
I figured out that the jsEvent is now located in event.jsEvent. This is where I can get the position from now.
This is my calendar initially after the event creation:
and this is after page refresh:
Can anyone help me understand how to keep the same style even after page refresh, specifically the vertical line in the middle? Sometimes the line increases as well.
The fullCalendar JS is as follows:
$('#calendar').fullCalendar({
locale: 'nl',
weekends: true, // false will hide Saturdays and Sundays
defaultView: 'agendaWeek',
allDaySlot: false,
slotLabelFormat: "HH:mm",
axisFormat: 'HH:mm',
timeFormat: 'HH:mm',
aspectRatio: 1,
editable: true,
eventStartEditable: true,
selectable: true,
selectHelper: true,
unselectAuto: false,
eventLimit: true,
dragScroll: true,
eventOverlap: false,
slotEventOverlap: false,
header: {
left: 'title',
center: '',
right: 'prev,next,today,agendaWeek,agendaDay'
},
views: {},
eventDrop: function (event, dayDelta, minuteDelta, allDay, revertFunc) {
var title = "Available";
var eventInfo;
eventInfo = {
title: title,
start: event.start,
end: event.end,
id: event.id
};
updateEvent(eventInfo);
},
eventResize: function (event, dayDelta, minuteDelta, revertFunc) {
var title = "Available";
var eventInfo;
eventInfo = {
title: title,
start: event.start,
end: event.end,
id: event.id
};
updateEvent(eventInfo);
},
select: function (start, end) {
var title = "Available";
var eventData;
if (title) {
eventData = {
title: title,
start: start,
end: end
};
$('#calendar').fullCalendar('renderEvent', eventData, true); // stick? = true
createEvent(eventData);
}
$('#calendar').fullCalendar('unselect');
},
eventSources: [{
events: function (start, end, timezone, callback) {
var employeeId = $("#employee-Id").val();
$.ajax({
url: '../' + employeeId + '/events',
dataType: 'json',
success: function (response) {
var events = $.parseJSON(response.data);
$('#calendar').fullCalendar('addEventSource', events);
callback(events);
}
});
}
}],
eventRender: function (event, element) {
//delete event on double click..Tanvir
element.one().bind('dblclick', function (e) {
/* e.preventDefault();
$(this).prop('disabled', true);
setTimeout(function () { $(this).prop('disabled', false); }, 500);*/
$("#startTime").html(moment(event.start).format('MMM Do YYYY, h:mm A'));
$("#endTime").html(moment(event.end).format('MMM Do YYYY,h:mm A'));
$("#eventContent").dialog({modal: true, title: event.title, width: 100});
$('.delete-event').bind('click', function (e) {
$('#calendar').fullCalendar('removeEvents', event._id);
deleteEvent(event.id);
$("#eventContent").hide();
});
$('.discard-delete').bind('click', function () {
$("#eventContent").hide();
});
});
},
});
});
No duplicate event is created in the database. What is happening a the event HTML is rendering twice after refresh. Specifically fc-time-grid-event under the event container class is rendering twice after refresh.
The cause of the duplication is here, in your events function:
$('#calendar').fullCalendar('addEventSource', events);
callback(events);
You are adding the event objects as an event source, AND sending them back to the calendar as a list of events, in the documented way. So while the events may not be duplicated in your database, your code is sending two sets of identical event data to the calendar, and consequently they are displayed twice.
You can simply remove this line:
$('#calendar').fullCalendar('addEventSource', events);
as it is totally unnecessary in this context.
If I move from a month with events to a month without events and then back again, my events are not re-appearing. Moving between months with events is working fine. I have to refresh the page to get them to come back. Here is my code and I just cannot see anything wrong...
$("#datepicker").fullCalendar({
header: {
left: 'prev,next,cbRefresh',
center: 'title',
right: ''
},
height: 750,
firstDay: 1,
weekNumbers: true,
editable: false,
eventLimit: true,
displayEventTime: false,
dayClick: function(seldate,jsEvent,view) {
window.location.href="multiple?dt="+seldate.format();
},
eventClick: function(evt,jsEvent,view) {
if(evt.id) {
window.location.href="details?id="+evt.id+"&dt="+evt.start.format();
}
},
events: {
url: servicePath+"calendar.php",
type: 'GET',
data: function() {
var theDate=$("#datepicker").fullCalendar('getDate');
return {
t: 'load',
uid: uid,
mon: theDate.month()+1
}
},
error: function(){
displayError("Events error: " +item.ErrorMessage);
}
},
eventRender: function(evt,ele,view) {
$(ele).each(function(){
$(this).attr("data-num",evt.start.format("YYYY-MM-DD"));
$(this).attr("data-worked",evt.worked);
$(".fc-title",this).attr("title",evt.activity);
});
},
eventAfterAllRender: function(view) {
if(view.name=="month") {
for(cDay=view.start.clone(); cDay.isBefore(view.end); cDay.add(1,'day')) {
var ttl=0;
var dateNum=cDay.format('YYYY-MM-DD');
$(".fc-event-container").find('.fc-event[data-num="'+dateNum+'"]').each(function(){
var currentWorked=$(this).attr("data-worked");
if(currentWorked) {
ttl+=parseInt(currentWorked);
}
});
//display the total and relevant buttons
if(ttl>0) {
var footer=$('.fc-day[data-date="'+dateNum+'"]').find(".fc-cell-footer");
$(footer).append('<a title="Delete all" class="btn btn-xs" href="delete?dt='+dateNum+'">'+
'<span class="glyphicon glyphicon-trash"></span></a>'+
'<a title="Copy" class="btn btn-xs" href="copy?dt='+dateNum+'">'+
'<span class="glyphicon glyphicon-copy"></span></a>'+
'<a title="List" class="btn btn-xs" href="list?dt='+dateNum+'">'+
'<span class="glyphicon glyphicon-th-list"></span></a>');
$(footer).append('<span class="ttlHours">Hours: '+buildTime(ttl)+'</span>');
}
}
}
},
dayRender: function(date,cell) {
var theDate=moment(date).format("YYYY-MM-DD");
$(cell).css("vertical-align", "bottom");
$(cell).append('<div class="fc-cell-footer"></div>');
},
customButtons: {
cbRefresh: {
text: 'Refresh',
click: function() {
$("#datepicker").fullCalendar("destroy");
buildCalendar();
}
}
}
});
Have even tried stripping the code back to the very basics and still not working. I do know the 'events: data' is being fired every time as I have it set to email me (while testing).
$("#datepicker").fullCalendar({
header: {
left: 'prev,next,cbRefresh',
center: 'title',
right: ''
},
height: 750,
firstDay: 1,
weekNumbers: true,
editable: true,
eventLimit: true,
displayEventTime: false,
events: {
url: servicePath+"calendar.php",
type: 'GET',
data: function() {
var theDate=$("#datepicker").fullCalendar('getDate');
return {
t: 'load',
uid: uid,
mon: theDate.month()+1
}
},
error: function(){
displayError("Events error: " +item.ErrorMessage);
}
},
customButtons: {
cbRefresh: {
text: 'Refresh',
click: function() {
$("#datepicker").fullCalendar("destroy");
buildCalendar();
}
}
}
});
Sounds like the URL you provide is filtering on the events that the calendar should show. Try something simple as:
$("#calendar").fullCalendar({
events: {
url: 'getcalendar.php',
type: 'POST',
data: {
},
success : function(response){
console.log("Updated events: "+response.length);
}
}
});
Php:
<?php
$start = $_POST['start'];
$end = $_POST['end'];
$query = mysqli(X,"SELECT * FROM calendar startdate >= '$start' AND enddate <= '$end'");
echo json_encode(mysqli_fetch_array($query,MYSQLI_ASSOC));
?>
I currently am using Adam Shaw's jQuery Calendar 'FullCalendar' and am experiencing significant delays in the calendar rendering. In short, the page appears, 1 second passes, the Calendar pops in, another second passes, and then the events populate the page, here. Is there a way to only fetch a certain number of events behind and before today's date? Or even loading the calendar immediately would be an improvement. I am also using Craig Thompson's qTip2.
Javascript
<script type=text/javascript>
// Setup FullCalendar
jQuery(document).ready
(function() {
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
var tooltip = $('<div/>').qtip({
id: 'fullcalendar',
prerender: true,
content: {
text: ' ',
title: {
},
},
events: {
render: function(event, api) {
var elem = api.elements.bgiframe;
}
},
position: {
my: 'bottom center',
at: 'top center',
target: 'event',
viewport: $(window),
adjust: {
mouse: false,
scroll: true,
method: 'shift',
resize: true
}
},
show: {
modal: {
on: false,
blur: true,
stealfocus: false
}
},
hide: false,
style: 'qtip-bootstrap'
}).qtip('api');
$('#fullcalendar').fullCalendar({
eventSources: ["https://www.google.com/calendar/feeds/emailaddresshere/public/basic",
"http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic"],
header: {
left: 'title',
center: '',
right: 'today prev,next'
},
selectable: true,
eventClick: function(data, event, view) {
var content = '<h3>'+data.title+'</h3>' +
'<p><b>Start:</b> '+data.start+'<br />' +
(data.end && '<p><b>End:</b> '+data.end+'</p>' || '');
tooltip.set({
'content.text': content
})
.reposition(event).show(event);
},
dayClick: function() { tooltip.hide() },
eventResizeStart: true,
eventDragStart: false,
viewDisplay: function() { tooltip.hide() }
});
}());
</script>