when I drag an external element on the calendar, I am not able to create an event on the calendar through it. eventReceive() callback is also not getting fired.
I have set the droppable option to true as well. Can someone please help? Have a look at the code:
HTML:
<div class="fc-event">Drag me !!</div>
JS:
$('.fc-event').each(() => {
// 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)
duration: '02:00',
create: true,
});
// 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
});
});
const calendarEl = document.getElementById('calendar');
calendar = new Calendar(calendarEl, {
themeSystem: 'standard',
selectable: true,
droppable: true,
columnHeader: true,
selectMirror: true,
editable: true,
plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin],
eventResourceEditable: true,
select(selectionInfo) {
showTaskCreationPopup(selectionInfo.start, selectionInfo.end);
},
eventReceive(element) {
console.log('event received');
createTask(element.event);
},
drop(info) {
console.log('dropped');
createTask(info.jsEvent);
},
eventDrop(info) {
console.log('event dropped');
},
header: {
left: 'prevYear prev today next nextYear',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay',
},
buttonText: {
// eslint-disable-next-line new-cap
prevYear: new moment().year() - 1,
// eslint-disable-next-line new-cap
nextYear: new moment().year() + 1,
},
});
calendar.render();
The way that I was able to achieve this was not through the eventReceive -neither eventDrop- event but using the 'drop' event. The signature is something like:
customDrop = ({ resource, date, draggedEl: { id } }) => {})
var containerEl = document.getElementById('external-events-list');
var eventEls = Array.prototype.slice.call(
containerEl.querySelectorAll('.fc-event')
);
eventEls.forEach(function(eventEl) {
if (eventEl.textContent == "Event1") {
new Draggable(eventEl, {
eventData: {
id: 1,
title: eventEl.innerText.trim(),
color: 'Orange',
textColor:'black'
}
});
} else if (eventEl.textContent == "Event2") {
new Draggable(eventEl, {
eventData: {
id: 2,
title: eventEl.innerText.trim(),
color: '#ccccff',
textColor:'black'
}
});
}}); // end eventEls.forEach
Related
I am managing appointments on fullcalendar. I have available slots of time where I can create new appointments. Also I can drag appointments onto available time slots which are shown as background events.
I only want the drop functionality on background event slots.
I have tried the eventOverlap method but it only works when the event is dropped on a background event. If the event is dropped elsewhere then I am unable to detect whether I am dropping the event on background event or on empty slot.
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
defaultView: 'timeGridWeek',
// height: 1080,
plugins: ['dayGrid', 'timeGrid', 'interaction'],
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
events: {
url: getUrl(),
failure: function() {
toastr.error('Unable to load calendar data, Please try later');
},
success: function() {
}
},
loading: function(bool) {
},
defaultDate: Date.now(),
editable: true,
eventLimit: true,
eventClick: function(info) {
if (info.event.extendedProps.type == 'Calendar') {
showCreateModal(info.event);
}
if (info.event.extendedProps.type == "Appointment") {
showUpdateModal(info.event);
}
},
eventOverlap: function(stillEvent, movingEvent) {
return stillEvent.extendedProps != '' && stillEvent.extendedProps.type === 'Calendar' ? true : false;
},
eventDrop: function(info) {
// check if event is being dropped on past date / slot
if (moment(info.event.start) < moment()) {
info.revert();
return;
}
// check if event is being dropped on future slot
if (moment(info.event.start) > moment()) {
swal({
title: "Are you sure?",
text: "You are about to re-schedule this appointment!",
icon: "warning",
// buttons: true,
buttons: ["No", "Yes !"],
dangerMode: true,
})
.then((response) => {
if (response) {
submitForm(false, true, info.event);
} else {
info.revert();
}
});
}
}
});
calendar.render();
This is what I want:
You're correct that eventOverlap doesn't help you here, because it's only triggered when the event is dropped onto a background event. It doesn't help you when the event is dropped somewhere else.
In fullCalendar 4 you can achieve what you need via the eventConstraint setting. This allows you to limit event dragging to specific windows of time. As the documentation says, you can provide a groupId value to this setting, and then
...events that are being dragged or resized must be fully contained by at least one of the events linked to by the given groupId.
All you need to do as well as that is give all your background events the same groupId.
For example, if you set:
eventConstraint: 1
and then have entries such as these within your event data:
{
start: "2019-07-10 09:00",
end: "2019-07-10 12:00",
rendering: "background",
groupId: 1
},
{
start: "2019-07-11 09:00",
end: "2019-07-11 12:00",
rendering: "background",
groupId: 1
},
This would mean that you would only be allowed to drag or resize an existing calendar event if you drag/resize it so that it falls entirely within the time periods covered by those background events which have a groupId of 1.
Here's a working demonstration: https://codepen.io/ADyson82/pen/jjdEjB
I'm using FullCalendar v3 (latest) with a PHP backend.
I'm returning a JSON array from the backend broken up into 2 arrays. The first contains event details (a list of orders for the day, with the order# as the title), and the second contains a daily summary (with a sum of orders and work hours as the title). The array looks like this:
{"events":[{"id":709989,"item_no":"ABC123","title":709989,"color":"red","start":"2019-05-14","end":"2019-05-14","allDay":true,"total_hours":3,"remaining_hours":1.5},{"id":709990,"title":709990,"item_no":"ABC345","color":"red","start":"2019-05-15","end":"2019-05-15","allDay":true,"total_hours":5.7,"remaining_hours":3.2}],"summary":[{"id":338823,"title":"Orders: 14\rHours:28.33\rRemaining Hours:13.33","start":"2019-05-14","end":"2019-05-14","allDay":true},{"id":338824,"title":"Orders: 3\rHours:14.2\rRemaining Hours: 12.2","start":"2019-05-15","end":"2019-05-15","allDay":true}]}
There are other properties but these are the basics.
What I'm trying to do is change the array that's used as the event source depending upon which view is selected. I've tried custom event rendering, custom view rendering, multiple event sources (even though it's expensive from a data point-of-view, the # of records aren't so numerous that it greatly effects performance).
The custom view name is customMonth. When this view is selected, I just want to render the summary data (from the summary array). If any of the other views are selected (I'm using basicWeek, month and listWeek), render the events array.
let vname;
$("#calendar").fullCalendar({
defaultDate: new Date(),
defaultView: 'month',
eventRender: function(eventObj, $el, view) {
let n = view.name;
if(n=='customMonth')
{
vname = 'customMonth';
$el.popover({
title: eventObj.title,
content: eventObj.total_hours,
html: true,
trigger: 'hover',
placement: 'auto',
container: 'body'
});
} else {
vname = n;
$el.popover({
title: "Work Order " + eventObj.title,
content: '<strong>Item#</strong>: ' + eventObj.item_no + '<br />' + '<strong>Total Hours</strong>: ' + eventObj.total_hours + '<br />' + '<strong>Remaining Hours</strong>: ' + eventObj.remaining_hours,
html: true,
trigger: 'hover',
placement: 'auto',
container: 'body'
});
}
},
events: function(start, end, timezone, callback){
$.ajax({
url: '/myendpoint',
type: 'POST',
dataType: 'json',
data: {
action: 'get-calendar-summary',
cell: selected_cell
},
success: function(data) {
let events = [];
if(vname=='customMonth') {
obj = data.summary;
$(obj).each(function() {
events.push({
id: $(this).attr('id'),
title: $(this).attr('title'),
start: $(this).attr('dept_due_dt'),
end: $(this).attr('dept_due_dt'),
total_hours: $(this).attr('total_hours'),
remaining_hours: $(this).attr('remaining_hours'),
order_count: $(this).attr('day_order_count'),
has_late_order: $(this).attr('has_late_order'),
allDay: true,
earliest_date: $(this).attr('earliest_date')
});
});
} else {
obj = data.event_results;
$(obj).each(function() {
events.push({
id: $(this).attr('id'),
color: $(this).attr('color'),
title: $(this).attr('title'),
start: $(this).attr('start'),
end: $(this).attr('end'),
earliest_date: $(this).attr('earliest_date'),
item_no: $(this).attr('item_no'),
total_hours: $(this).attr('total_hours'),
remaining_hours: $(this).attr('remaining_hours')
});
});
}
callback(events);
},
error: function(err) {
console.log(err);
}
});
},
views: {
customMonth: {
type: 'month',
buttonText: 'overview'
}
},
viewRender: function(view, el) {
let lastview;
if(view.name=='customMonth') {
if(lastview == 'customMonth') {
return false;
} else {
view.unrenderDates();
view.renderDates();
$("#calendar").fullCalendar('rerenderEvents');
}
lastview = 'customMonth';
} else {
if(lastview=='customMonth') {
view.unrenderDates();
view.renderDates();
$("#calendar").fullCalendar('rerenderEvents');
}
lastview = view.name;
}
},
header: {
left: 'prev,next',
center: 'title',
right: 'basicWeek,month,listWeek,customMonth'
},
themeSystem: 'bootstrap3',
timeZone: false,
weekends: false,
//tried with and without lazyFetching
lazyFetching: true
});
I'd appreciate any guidance. I've searched StackOverflow (this seems like the closest, but I followed exactly and it didn't work (switching out viewDisplay for viewRender)), Github, and all other sources I can think of.
Here a simplified example of what you are trying to achieve (I hope I well understand your problem) :
HTML :
<div id='calendar'></div>
Javascript :
$(document).ready(function() {
var ev1 = {"events":[{"id":709989,"item_no":"ABC123","title":'Event from source 1',"color":"red","start":"2019-05-14","end":"2019-05-14","allDay":true,"total_hours":3,"remaining_hours":1.5}]};
var ev2 = {"events":[{"id":709989,"item_no":"ABC123","title":'Event from source 2',"color":"blue","start":"2019-05-14","end":"2019-05-14","allDay":true,"total_hours":3,"remaining_hours":1.5}]};
$('#calendar').fullCalendar({
defaultDate: new Date(),
defaultView: 'month',
viewRender: function(view) {
if(view.type === 'basicWeek') {
$('#calendar').fullCalendar( 'removeEventSource', ev1 );
$('#calendar').fullCalendar( 'addEventSource', ev2 );
return;
}
},
header: {
left: 'prev,next',
center: 'title',
right: 'basicWeek,month,listWeek,customMonth'
},
});
$('#calendar').fullCalendar( 'addEventSource', ev1 );
});
And the codepen to try it.
It uses the callback function viewRender() to detect when a view change and addEventSource()/removeEventSource() to change the data. So when you change the view from month to week it will change the source events.
I need to manually resize the event in fullcalendar without the code editable=true, because I have added a dropdown inside the event and
to work its click I suppose prevent the editable property(editable=false). Now the dropdown is working but the resize property been disappeared. Even I tried the property resizable(resizable=true). How can I bring both the codes?
sample code
$('#calendar').fullCalendar({
theme: true,
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay,agendaFourDay'
},
views: {
agendaFourDay: {
type: 'agenda',
duration: { days: 4 },
buttonText: '4 day'
},
},
defaultView: viewDefault,
//minTime: '07:00:00',
//maxTime: '21:00:00',
allDaySlot: false,
eventClick: updateEvent,
selectable: true,
selectHelper: true,
select: selectDate,
editable: false,
resizable: true,
events: "JsonResponse.ashx?eids=" + eIds + "&searchText=" + searchText,
//events: {
// url: 'JsonResponse.ashx',
// type: 'POST',
// data: {
// eIds: $('#hdnCustomerIds').val()
// },
// error: function () {
// alert('there was an error while fetching events!');
// }
//},
defaultDate: dateDefault,
eventDrop: eventDropped,
eventResize: eventResized,
eventRender: function (event, element, view) {
debugger;
// event.stopPropagation();
$(element).find('.fc-title').prepend('' + event.customerid + ',');
$(element).find('.fc-title').html('<select class="dropdown" style="color:black";" onchange="OnchangeEventStatus(this,'+event.id+')"><option >Volvo</option><option value="saab">Saab</option><option value="opel">Opel</option><option value="audi">Audi</option></select>');
$(element).find('.dropdown').click(function (e) {
e.stopImmediatePropagation(); //stop click event, add deleted click for anchor link
});
$(element).find('.delete-event-link').click(function (e) {
e.stopImmediatePropagation(); //stop click event, add deleted click for anchor link
window.top.location.href = "/Sr_App/Customer_Profile.aspx?CustomerId=" + event.customerid;
});
//$(element).find('.fc-title').prepend('' + event.customerid + ',');
element.qtip({
content: {
text: qTipText(event.start, event.end, event.description),
title: '<strong>' + event.title + '</strong>'
},
position: {
my: 'bottom left',
at: 'top right'
},
style: { classes: 'qtip-shadow qtip-rounded' }
});
}
});
i got the solution, just add the property editable: false for fullCalendar
$('#calendar').fullCalendar({
selectable: true,
editable: false,
events: "JsonResponse.ashx?eids=" + eIds + "&searchText=" + searchText,
......
....
});
My popover isn't working when I trigger an evenLimitClick event in my fullcalendar. It works fine in my dayClick event, but nothing happens in my eventLimitClick. Here is my fiddle
I even tried setting the popover to be called on the parent (ex. $(this).parent().popover, but that didn't work either.
$(document).ready(function () {
// page is now ready, initialize the calendar...
var eventsArray = [{
title: 'Test1',
start: new Date()
}, {
title: 'Test2',
start: new Date("2015-04-21")
}, {
title: 'Test3',
start: new Date("2015-04-21")
}];
$('#calendar').fullCalendar({
// put your options and callbacks here
header: {
left: 'prev,next', //today',
center: 'title',
right: ''
},
defaultView: 'month',
editable: true,
allDaySlot: false,
selectable: true,
events: eventsArray,
eventLimit: 1,
eventLimitClick: function (cellInfo, jsEvent) {
$(this).popover({
html: true,
placement: 'bottom',
container: 'body',
title: function () {
return $("#events-popover-head").html();
},
content: function () {
return $("#events-popover-content").html();
}
});
$(this).popover('show');
},
dayClick: function (cellInfo, jsEvent) {
$(this).popover({
html: true,
placement: 'bottom',
container: 'body',
title: function () {
return $("#events-popover-head").html();
},
content: function () {
return $("#events-popover-content").html();
}
});
$(this).popover('show');
},
})
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/2.3.1/fullcalendar.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/2.3.1/fullcalendar.js"></script>
<div style="border:solid 2px red;">
<div id='calendar'></div>
<div id="events-popover-head" class="hide">Events</div>
<div id="events-popover-content" class="hide">Test</div>
</div>
In the eventLimitClick event change following:
$(this).popover
To
$(cellInfo.dayEl)
cellInfo.dayEl is the clicked Day cell element for which you want to show popover.
I have updated the Fiddle also
I'm having 2 weird issues with my popovers shown here in fiddle
After I have selected a day (in fullcalendar) and the popover shows, I click the day again to hide the popover, then I click again in the same day to show again, but no popover!. It's after I click to show, hide, then click to show again that I'm having issues.
When I click on an eventLimitClick event inside a day (April 20th in fiddle), then click on the day (April 20th). The popover hides after I click the day, but then doesn't show any popover for the day like it should.
Here is some of the logic, but please refer to the fiddle link above for working code.
var $calPopOver;
$('#fullcalendar').fullCalendar({
header: {
left: 'prev,next', //today',
center: 'title',
right: ''
},
defaultView: 'month',
editable: true,
allDaySlot: false,
selectable: true,
eventLimit: 1,
events: function(start, end, timezone, callback) {
$.ajax({
url: '/ManageSpaces/GetDiaryEvents/',
dataType: 'json',
data: {
start: start.format(),
end: end.format(),
id: $("#HiddenYogaSpaceId").val()
},
success: function(doc) {
var events = [];
$(doc).each(function() {
events.push({
title: "1 Event", //$(this).attr('title'),
start: $(this).attr('start') // will be parsed
});
});
callback(events);
}
});
},
eventLimitText: function(numberOfEvents) {
return numberOfEvents + " Events";
},
eventLimitClick: function(cellInfo, jsEvent) {
$(cellInfo.dayEl).popover({
html: true,
placement: 'bottom',
container: 'body',
title: function() {
return $("#events-popover-head").html();
},
content: function() {
return $("#events-popover-content").html();
}
});
//$(cellInfo.dayEl).popover('toggle');
if ($calPopOver) {
$calPopOver.popover('destroy');
}
$calPopOver = $(cellInfo.dayEl).popover('show');
},
eventClick: function(calEvent, jsEvent, view) { //function (data, event, view) {
//var s = cellInfo.segs;
$("#eventDetails.collapse").collapse('toggle');
if ($calPopOver)
$calPopOver.popover('destroy');
},
dayClick: function(data, event, view) {
$dayClickedDate = data.format();
$(this).popover({
html: true,
placement: 'bottom',
container: 'body',
title: function() {
return $("#day-popover-head").html();
},
content: function() {
return $("#day-popover-content").html();
}
});
//$(this).popover('toggle');
if ($calPopOver) {
$calPopOver.popover('destroy');
}
$calPopOver = $(this).popover('show');
}
});
Maybe you can look into these bootstrap examples. If you can manage to get a copy of the examples, it's simple a copy and paste scenario IMO.
http://getbootstrap.com/javascript/
Search for "Popovers popover.js" on this page and scroll a bit down for the examples.