I can't believe nobody asked this. This is driving me insane. Im using FullCalendar to let the user drop external events to the calendar. I´m folliwing the well known approach:
$('#external-events div.external-event').each(function () {
var eventObject = {
type: 2,
id: $(this).attr("data-id"),
title: $(this).attr("data-name"),
duration: $(this).attr("data-duration"),
guid: $(this).attr("data-guid"),
color: $(this).attr("data-color")
};
// store the Event Object in the DOM element so we can get to it later
$(this).data('eventObject', eventObject);
// make the event draggable using jQuery UI
$(this).draggable({ zIndex: 999, revert: true, revertDuration: 0 });
});
My calendar is configured like this (drop event):
drop: function(date) {
// retrieve the dropped element's stored Event Object
var originalEventObject = $(this).data('eventObject');
// we need to copy it, so that multiple events don't have a reference to the same object
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date.format();
// render the event on the calendar
//$('#calendar').fullCalendar('renderEvent', copiedEventObject, true);
$.ajax({
async: false,
url: '#Url.Action("AddWorkoutToPlan", "Planning")',
data: { 'planId': planId, 'workoutId': copiedEventObject.id, 'date': date.format() },
success: function(data) {
$('#calendar').fullCalendar('refetchEvents');
}
});
},
As you can see, I don't render the event, I just make an ajax call and on success I refetch the events so I can get the DB id, in case the user wants to remove it.
This is how I get my events:
events: {
url: '#Url.Action("GetPlannedActivities", "Planning")',
data: function() { return { planId: '#Model.Id' } },
beforeSend: function(xhr, opts) {
if (!$("#selectedPlan").val()) {
xhr.abort();
unblockContainer($("#calendar"));
}
},
success: function(data) {}
},
This is working nice, but if the user moves from the current month, then the external events wont drag nor the drop callback is triggered... I don't know what is going wrong...
Any ideas?
Finally I rolled back the FullCalendar version from 2.1.0_BETA1 / BETA2 to v2.0.2 and now is working as expected.
So I guess this is a bug in the new version that uses DIVS instead of TABLES.
Related
I want to create some events on my DayPilot scheduler using my own modal window. It works fine when creating the first event. After that the event gets doubled, tripled and so on along with the new created events. It seems like args is not getting empty.
Here is my code for onTimeRangeSelected:
dp.onTimeRangeSelected = function (args) {
$('#myModal').show();
$('.schedule_option').click(function(){
$('#myModal').hide();
dp.clearSelection();
var txt=this.getAttribute("arg");
var colour = this.style.backgroundColor;
var e = new DayPilot.Event({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: txt,
backColor:colour
});
var params = {
start: args.start,
end: args.end,
text: txt,
backColor:colour,
id:DayPilot.guid(),
resource: args.resource
};
DayPilot.Http.ajax({
url: "admin/backend_create.php",
method: "POST",
data: params,
success: function(ajax) {
dp.message("succes");
},
error: function(ajax) {
dp.message("Saving failed");
}
});
dp.events.add(e);
});
}
And the effect I am getting is:
The modal contains 3 buttons corresponding to each event to be scheduled type.
If I use DayPilot modal I don't have issues. I can't find an explanation because somehow it worked until last Friday.
Thanks!
Ok. I found the issue. It's the click event handler that it is being added every time a time range it's selected. I will have to get it out of there and find another way to use my own modal.
I am trying to implement a Fullcalendar functionality like so: an external event is dragged to the calendar, then the event is saved to the database via ajax, with its title, start and end dates.
I did achieve making the event title and start date/time to be saved to the DB, however I cannot fix the following couple of issues:
Currently, the end date/time saved to the DB is the same as the start date/time.
How do I make the end date to be 24h after the start date for all day events?
How do I get the exact start and end dates for a fixed duration event?
Once the event is dragged and saved to the DB, I cannot drag-drop any more events until I hit F5
My HML is the following:
<div id='external-events'>
<div class="fc-events-container">
<div>All Day Events</div>
<div class='fc-event' data-color='#28A745'>All Day Event</div>
<div>Fixed Duration Events</div>
<div class='fc-event' data-color='#50C1E9'>Event From 8am till 5 pm</div>
</div>
</div>
JQuery:
$('#external-drag').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
editable: true,
droppable: true,
defaultDate: new Date(),
selectable: true,
events: "../../ajax/get_events.php",
drop: function( date, allDay, jsEvent, ui ) {
var start = date.format();
var end = date.format();
var title = $.trim($(this).text());
$.ajax({
url: '../../ajax/add_event.php',
data: 'title='+ title+'&start='+ start +'&end='+ end,
type: "POST",
success: function(json) {
alert('Added Successfully');
},
error: function() {
alert('Error');
}
});
calendar.fullCalendar('renderEvent',
{
title: title,
start: start,
end: end,
allDay: allDay
},
true
);
}
});
Firstly, some background context to what you're experiencing: although you've labelled your second event as "from 8 to 5pm", that has no actual practical effect. If you drop either of your draggable items onto a time-aware calendar view (e.g. week view) then it will gain a start date and time. On a view such as month, which has no concept of specific times of day, then it will just gain a start date, without a time attached.
However in both cases, there is no end date. Once you add the event to the calendar via renderEvent, then fullCalendar will assign it a default duration (as per the
defaultAllDayEventDuration or defaultTimedEventDuration settings, as appropriate). You can use that to work out the notional end date (although the event object doesn't actually have its end property set unless you set forceEventDuration to true).
Now to a solution:
If you want to control it so that specific dragged items have pre-defined times, you would have to specify the time data in the data- properties of the relevant draggable items. Then you need to get those values in your drop callback, the same way as you do with the title, and use them when creating your event.
It took a bit of fiddling to get to this point, especially with the moment objects, but it now appears to do what you want - as far as I understand it.
drop: function(date, jsEvent, ui) {
var element = $(this);
var title = $.trim(element.text());
var color = element.data("color");
var start = moment(date.format()); //lose the extended fullCalendar moment with its "ambiguously-timed" feature, which gets in the way here
var end = start.clone();
var allDay = true;
console.log(start.format(), end.format());
if (typeof element.data("starttime") !== 'undefined') {
//timed events
var starttime = moment.duration(element.data("starttime"));
var endtime = moment.duration(element.data("endtime"));
start.set({ "hour": starttime.hours(), "minute": starttime.minutes()});
end.set({ "hour": endtime.hours(), "minute": endtime.minutes()});
allDay = false;
console.log(starttime.hours());
} else {
//allday events
end.add({ days: 1 });
}
console.log("title=" + title + "&start=" + start.format() + "&end=" + end.format());
$.ajax({
url: '../../ajax/add_event.php',
data: 'title='+ title+'&start='+ start.format() +'&end='+ end.format(),
type: "POST",
success: function(json) {
alert('Added Successfully');
},
error: function() {
alert('Error');
}
});
calendar.fullCalendar(
"renderEvent",
{
title: title,
start: start,
end: end,
color: color,
allDay: allDay
},
true
);
}
Demo: https://codepen.io/ADyson82/pen/GRRXwRL?editors=1010
N.B. Note that if a user drags an event onto a time-aware view like the week view, it will still create the events exactly as per the settings in the draggable, and will ignore whatever time of day they dropped it on. It's possible this could be confusing or irritating to some users - but I guess it depends what you're trying to achieve exactly.
P.S. I don't know where you got the idea that the drop callback has an allDay parameter. It's clearly not there in the documentation. You can't use that, so I've removed it in my version.
In the Full calendar external-dragging events i want to change the description by adding the id of the event inserted in the database with the description while copying the event.
droppable: true, // this allows things to be dropped onto the calendar !!!
drop: function(date, allDay){ // this function is called when something is dropped
// retrieve the dropped element's stored Event Object
var originalEventObject = $(this).data('eventObject');
// we need to copy it, so that multiple events don't have a reference to the same object
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date;
copiedEventObject.description = 'custom_event';
copiedEventObject.allDay = allDay;
copiedEventObject.className = $(this).attr("data-class");
var new_date = copiedEventObject.start.format();
var new_event = copiedEventObject.title;
$.post('calendar_action.php',{'action':'create_event','date':new_date,'event':new_event},function(data){
$(copiedEventObject).attr("description",data);
});
// render the event on the calendar the last `true` argument determines if the event "sticks" (http://arshaw.com/fullcalendar/docs/event_rendering/renderEvent/)
$('#calendar').fullCalendar('renderEvent', copiedEventObject, true);
$(this).remove();
},
The $.post method in ajax is not passing the global variable so i used the $.ajax method and its working fine
$.ajax({
type: 'POST',
async: false,
url: "calendar_action.php",
data: {'action':'create_event','date':new_date,'event':new_event},
success: function(data) {
desc = data;
}
});
copiedEventObject.description = desc+'_custom_event';
I'm a bit lost where is the proper place to make this with the Full Calendar documentation and need some fiddle to point me into the right way. When my calendar loads the events, before them show up on the calendar, I need to make a ajax call into a wordpress db which should return a date of a post. Each post is an event on the calendar. Depending on the response of the database, if the post date is in future time than the current time, then the calendar should show this event in an specific color, if it is past or current time it should be another different specific color.
So basically, before each event renders on the calendar I need to trigger an ajax call for each of them and evaluate the data returned to apply them the right color for past/current events and future events.
Someone experienced with Full Calendar could point me a fiddle with an example how is this done within the Full Calendar documentation?
This is the code I went so far. I am looking to stay in the loop with the calendar refetchEvents and be able to fetch with ajax in the background data from the posts of a WordPress website to use it on the next refetchEvents trigger and so on.
$(function () {
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
var webData = null; //array() data
$('#calendar-holder').fullCalendar({
eventRender: function(event, element, webData) {
var dataHoje = new Date();
/*
Use webData data taken with ajax on eventAfterAllRender callback option
inside this conditional statements to draw on the event box
colors and text values depending on the status and date of the post returned.
*/
if (event.start < dataHoje && event.end > dataHoje) {
element.css('background-color', '#FFB347');
element.find('.fc-event-inner').append('<span class="fc-event-status">ON AIR</span>');
} else if (event.start < dataHoje && event.end < dataHoje) {
element.css('background-color', '#77DD77');
element.find('.fc-event-inner').append('<span class="fc-event-status">Published</span>');
} else if (event.start > dataHoje && event.end > dataHoje) {
element.css('background-color', '#AEC6CF');
element.find('.fc-event-inner').append('<span class="fc-event-status">Schedued</span>');
}
},
eventAfterAllRender: function () {
webData = '(AJAX CALL TO WEBSITE POSTS I THINK SHOULD GO HERE)';
console.log(webData);
},
eventColor: '#378006',
complete: function() {
},
defaultView: 'basicDay',
googleCalendarApiKey: 'AIzaSyCtEQZsFtsY41kJ1Av5FftgX9kdfkHKH',
events: {
googleCalendarId: 'mywebsite.com_l84tadr5fulc7j0628g3g6oj3k#group.calendar.google.com'
},
header: {
left: 'prev, next',
center: 'title',
right: 'basicDay, basicWeek, month, '
},
lazyFetching: true,
timeFormat: {
agenda: 'h:mmt', // 5:00 - 6:30
'': 'h:mmt' // 7p
},
weekNumbers: false,
lang: 'en',
eventSources: [
{
url: Routing.generate('fullcalendar_loader'),
type: 'POST',
data: {
},
error: function() {
}
}
]
});
});
var refreshRate;
function reloadTime() {
refreshRate = setTimeout(reloadPage, 5000);
}
function reloadPage() {
$("#calendar-holder").fullCalendar("refetchEvents");
reloadTime();
}
$( document ).ready(function() {
reloadTime();
});
Changing the color:
The way you did it works, but the easiest way is to do it in eventDataTransform. Like so:
eventDataTransform: function(eventData){
if(eventData.end.isBefore(moment())){
eventData.color = "black";
}else{
eventData.color = "green";
}
return eventData;
},
Color Demo
Check if event exists
You didn't mention exactly what to do if the database returns false, but I'll assume you don't want nonexistent events rendered.
Since the source for your events is google calendar, this is actually kind of tricky. Normally, you would use the custom events function and do two ajax calls in it (one for the events and one for checking if they are valid). But you can't do this with google cal events.
So instead we will use eventDataTransform and only display the events after we know they exist.
eventDataTransform: function(eventData){
eventData.display = false; //Don't display until we check the server
eventData._uid = idCounter++; //unique ID. Don't need this if they already have unique IDs
ajaxCall(eventData); //check the server (will retroactively update the event to be displayed)
if(eventData.start.isBefore(moment())){ /*...*/ } //colors
return eventData;
},
The top of your eventRender callback should look like:
eventRender: function(event,element){
if(!event.display){ //Render only if the event exists
return false; //return false to stop the event from rendering.
}
/*...your other render code if you have any*/
}
Define your ajaxCall function outside of fullcalendar:
var ajaxCall = function(eventData){
$.get( "ajax/test.html", function( data ) {
setEvent(eventData._uid,data); //data should be a boolean
});
};
var setEvent = function(id,exists){
var fcEvent = $('#calendar').fullCalendar("clientEvents",function(event){ //get the associated event object
if(event._uid === id){
return true;
}
})[0];
if(typeof fcEvent !== "object")$.error("Event id "+id+" doesn't exist!"); //Throw error if it doesn't exist
fcEvent.display = exists; // Store the server response in the event
$('#calendar-holder').fullCalendar("updateEvent",fcEvent); // Updates and re-renders the event
}
JSFiddle Demo (using fake ajax calls)
Some Explanation
Couple of things that might be useful to know:
The word render in fullcalendar refers to actually displaying the events. It's done whenever the view changes (more often than events are fetched from the DB)
Event sources only fetch events when they are needed. They are stored client-side as data that can be rendered as needed.
eventDataTransform is called once after an event source retrieves an event.
So if you put your ajax call in eventAfterAllRender, the ajax call would be done everytime FC decided to render the calendar resulting in more ajax calls that necessary. It also means you would get a delay every time you change the view. It's much better to do it earlier than render-time.
I have the following js:
!function ($) {
$(function(){
// fullcalendar
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
var addDragEvent = function($this){
// create an Event Object (http://arshaw.com/fullcalendar/docs/event_data/Event_Object/)
// it doesn't need to have a start or end
var eventObject = {
title: $.trim($this.text()), // use the element's text as the event title
className: $this.attr('class').replace('label','')
};
// store the Event Object in the DOM element so we can get to it later
$this.data('eventObject', eventObject);
// 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
});
};
$('.calendar').each(function() {
$(this).fullCalendar({
header: {
left: 'prev,next',
center: 'title',
right: 'today,month,agendaWeek,agendaDay'
},
editable: true,
droppable: true, // this allows things to be dropped onto the calendar !!!
drop: function(date, allDay) { // this function is called when something is dropped
// retrieve the dropped element's stored Event Object
var originalEventObject = $(this).data('eventObject');
// we need to copy it, so that multiple events don't have a reference to the same object
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date;
copiedEventObject.allDay = allDay;
// render the event on the calendar
// the last `true` argument determines if the event "sticks" (http://arshaw.com/fullcalendar/docs/event_rendering/renderEvent/)
$('#calendar').fullCalendar('renderEvent', copiedEventObject, true);
// is the "remove after drop" checkbox checked?
if ($('#drop-remove').is(':checked')) {
// if so, remove the element from the "Draggable Events" list
$(this).remove();
}
}
,
events: [
],
eventClick: function(event) {
alert('win');
}
});
});
getEvents();
});
}(window.jQuery);
function getEvents()
{
$.ajax({
type: 'POST',
url: '/Calendar/findEvents',
dataType: 'json',
data: {
request: 'ajax'
},
success: function (data)
{
if(data.length > 0)
{
for (index = 0; index < data.length; ++index)
{
var d = new Date(data[index]['end']);
if(data[index]['is_online'] === 1)
{
var myevent = {title: 'Forløb: '+data[index]['academy_name'].toUpperCase()+' \n Modul: '+data[index]['module_name']+ '\n Type: E-learning',start: new Date(d.getFullYear(), d.getMonth(), d.getDate())};
}
else
{
var myevent = {title: 'Forløb: '+data[index]['academy_name'].toUpperCase()+' \n Modul: '+data[index]['module_name']+ '\n Type: Kursus'+ '\n Lokation: '+data[index]['location']+'\n Underviser: '+data[index]['mentor'],start: new Date(d.getFullYear(), d.getMonth(), d.getDate())};
}
$('.calendar').fullCalendar( 'renderEvent', myevent, true);
}
}
}
});
}
As you can see when the calendar is loaded i am starting to load events (through ajax) into the calendar.
Now what i want to do is simply add an eventListner on each of the elements.
In the documentation it sates the following:
eventClick: function(event) {
if (event.url) {
window.open(event.url);
return false;
}
}
Which i attempted with just a simple alert (as you can see in the code:
eventClick: function(event) {
alert('win');
}
However when i click my items nothing happens.
Can anyone tell me what i am missing?
I know you are loading events through AJAX, but have you tried returning an array of objects (the events) to the events array in your instantiation of the calender? Right now you are passing an empty array, so the plugin is not assigning any elements as 'events', and thus isn't assigning any click handlers.
events: [ getEvents()
],
eventClick: function(event) {
alert('win');
}
});
And then inside your getEvents() function call, rather than render the events, you should just return the event objects.
The suggested way to load events with an ajax call + some manipulation on the data you receive is to use your function as an event source (link to the doc) :
$(this).fullCalendar({ ...
events: function(start, end, tz, callback) {
$.ajax({
type: 'POST',
url: '/Calendar/findEvents',
dataType: 'json',
data: {
request: 'ajax'
},
success: function (data) {
// build an array of event objects with the data
var events = ...
// use the "callback" argument to load them in the grid :
callback(events);
}
});
},
...
});
note : the signature of the function depends on the version of fullcalendar you are using. Versions prior to version 2.0 do not have the tz argument (again, check the doc).
FullCalendar is processing your listeners with no events. Your ajax is loaded after the initialization of your calendar. You could keep your current code and add the listener on eventRender.
$('#calendar').fullCalendar({
eventRender: function(event, element, view){
element.click(function(){
alert('test');
})
}
});
I would probably suggest loading the events as suggested in the other answers though, but this should work.
$('#calendar').fullCalendar({
eventRender: function(event, element, view){
element.click(function(){
alert('test');
});
$("#calendar .fc-helper-container").find("a").remove();
}
});