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.
Related
I'm developing a Calendar application via fullcalendar.
I'm currently working on a mini sized calendar. The mini calendar will not display the events.
I'm trying to use tooltip instead. so when the user will mouseover a specific daycell - all the events of the specific daycell will be displayed via tooltip. (this is my issue)
I been working on this issue for almost two days now.
unfortunately, full calendar only offers "eventMouseover". (no dayMouseover available).
Also, using $(".fc-day").hover is not really the best way to go because it is working only when hovering the bottom of the cell.
there is no documentation for this on the web so far.
Anybody knows which is the best way to Tackle an issue?
here is my code so far:
$("#miniCalendar").fullCalendar({
defaultDate: currentDate,
viewRender: function (view, element)
{
monthStart = view.intervalStart.format("YYYY-MM-DD");
monthEnd = view.intervalEnd.subtract(1, "days");
monthEnd = view.intervalEnd.format("YYYY-MM-DD");
mStart = view.intervalStart.format("M");
yStart = view.intervalStart.format("YYYY");
},
events: function (start, end, timezone, callback) { //custom events function to be called every time the view changes
$.ajax({
url: getMonthDataUrl,
type: "post",
data: {
startDate: monthStart,
endDate: monthEnd,
custom_config: Config
},
error: function () {
//alert("there was an error while fetching events!");
},
success: function (data) {
calendarData = data;
console.log(calendarData);
thingsToDoAfterWeLoadCalendarData(calendarData);
callback(eventsJsonArray); //pass the event data to fullCalendar via the supplied callback function
}
});
},
fixedWeekCount: false,
dayRender:
function (date, cell) {
//the events are loaded vie eventAfterAllRender function
//eventAfterAllRender takes time to load. so we need dayRender function in order to give the calendar default colors until the real colors are loaded
// the default colors spouse to look like the correct colors. this is needed for a better looking calendar while loading new events
if (!cell.hasClass("fc-other-month")) {
//that means this is a cell of this current month (becuase only cells that belong to other month have the "fc-other-month" class
var weekDay = date.format("dddd");
if (weekDay == "Saturday" || weekDay == "Friday") {
cell.css("background-color", "#edf5f9");
} else{
//regular days
cell.css("background-color", "#f7fafc");
}
} else{
//cells that belong to the other months
$(".fc-other-month").css("background-color", "#ffffff");
}
},
eventAfterAllRender:
(function(view, event, element) {
let viewDisplay = $("#miniCalendar").fullCalendar("getView");
if (viewDisplay.name == "month") { //checking if this the month view. this is needed for better display of the week\day view (if we ever want to use it)
$(".fc-day:not(.fc-other-month)").each(function(index, element) {
//loop through each current month day cell
$(this).empty(); //removing old icons in case they are displayed
let cellDate = moment($(this).data("date")).format("YYYY-M-D"); // "YYYY-M-D" date format is the key in the json_backgrundColorBigCalendar array
$(this).css("background-color", json_backgrundColorBigCalendar[cellDate]); //set the background colour of the cell from the json_backgrundColorBigCalendar array.
});
}
$(".fc-other-month").css("background-color", "#ffffff"); //days that belong to other month gets a color background that will show this days are irrelevant
}),
dayClick: function(date, jsEvent, view) {
window.location = fullCalendarUrl;
},
});
I dont really know if there is a "fullcalendar-way" doing this, but why can't you use your hover event listener and just also let it listen on fc-day-top hover?
$(".fc-day, .fc-day-top").hover(function(e) {
console.log($(e.currentTarget).data("date"));
});
This will also work if you hover the top of a day cell.
Update:
To get the events on hover use this:
var $calendar = $("#calendar");
$(".fc-day, .fc-day-top").hover(function(e) {
var date = moment.utc($(e.currentTarget).data("date"));
var events = $calendar.fullCalendar("clientEvents", function(event) { return event.start.startOf("day").isSame(date); });
console.log(events);
});
(Tested on the calendar on the fullcalendar main site).
Of course you have to change the jQuery selector of the calendar (#calendar) as it is in your code.
I've never seen full calendar before, but if I understand correctly you want to bind a hover function to custom days of the calendar. Is that correct?
If so you can simply select days of the calendar with their "data-date" attribute. So something like the code below would let you specify a hover function for a desired day:
$("[data-date='2017-10-10']").hover(function(e) {
console.log("You moused over 10/10/2017!");
});
I am developing an application in angularJS. I get data from a rest service in the controller. And then I pass that data to the view using $scope. In my view, there is an inpage javascript (I know its a bad practice) and I want to used the data passed in my inpage javascript. Simply using the variable name or {{variable_name}} doesnt work. Can any one give any suggestions?
Here is my code snipper from controller:
$scope.requests = null;
var url = 'my_url';
$http.get(url).then(function(response)
{
$scope.requests = response.data;
if (response.data.status && response.data.message)
{
var status = response.data.status + '!';
var message = response.data.message;
showAlert(status,message);
}
return;
}).catch(function(response)
{
showAlert('danger!','Some error occured. Please try again.');
});
And here is my inpage javascript code:
<script>
$(document).ready(function() {
/*
date store today date.
d store today date.
m store current month.
y store current year.
*/
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
/*
Initialize fullCalendar and store into variable.
Why in variable?
Because doing so we can use it inside other function.
In order to modify its option later.
*/
$('#calendar').fullCalendar({
// put your options and callbacks here
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
defaultView: 'month',
/*
selectable:true will enable user to select datetime slot
selectHelper will add helpers for selectable.
*/
selectable: false,
selectHelper: false,
/*
editable: true allow user to edit events.
*/
editable: false,
/*
eventStartEditable: false doesnt allow the dragging of events
*/
eventStartEditable: false,
/*
eventOverlap: false doesnot allow overlapping of events
*/
eventOverlap: false,
/*
events is the main option for calendar.
for demo we have added predefined events in json object.
*/
/* var events = requests.map(function(obj, index)
{
if (obj.accepted == 'no' ) return false;
return { id : obj.id, start : obj.start, end : obj.end }
})*/
});
});
</script>
<div id='calendar'></div>
Using {{requests}} prints out the data but it CAN NOT be used within the <script> </script> tags. I want to use it with the script tags
In angular you want to include the $window service in your controller, then set a variable on the $window object. The script tag can then check for window.requestsVariable. You are going have to handle the asynchronous nature of the request though, since the request variable will be empty till the request returns.
Further, as andrew.butkus pointed out, you probably don't want to do this. The jquery code could be simplified if you moved it into the linking function of an angular directive for the calendar, IMO.
Declare a global variable as var gloable; outside the controller and inside the controller assign whatever value you want to the declared global variable and then later you can access it inside your Jquery onready function.
I have the following markup:
I have a instance of fullcalendar.
When clicking on a day (triggering the dayClick-callback), a bootstrap modal is opened, where the user can enter a title, and the start/end date. Once clicking on ok, those values provided, will be added to the calendar. Here's the code for that:
function addTitle(){ //having a button onClick="addTitle()"
var title = $('#add_date_title').val();
var startdate = $('#add_date_startdate').val();
var enddate = $('#add_date_enddate').val();
var end_split = enddate.split('-');
end_split[2]= parseInt(end_split[2])+parseInt("1");
enddate = end_split[0] + "-" + end_split[1] + "-" + end_split[2];
$('#add_date_title').val('');
$('#add_date_startdate').val('');
$('#add_date_enddate').val('');
$('#add_date_modal').modal('hide');
var myCalendar = $('#calendar');
var myEvent = {
title:title,
allDay: true,
start: startdate,
end: enddate
};
myCalendar.fullCalendar( 'renderEvent', myEvent );
}
So the event is now in the calendar. But when e.g. switching the month, or reloading the page, all data is lost, of course, because it's saved nowhere.
Now the question is: How could I save the event directly into the database, and then load it, so where can I bring in php code, to save the event to a db... The problem, why I'm asking, is that the site in between adding events is never reloaded, so I'm not able, to check for GET or POST-Parameters or something similiar... Could I maybe do this with AJAX? If yes, how? Because I'm not really familiar with AJAX.
You can actually save the events in DB.
Use this ajax after your modal trigger.
Get the values like title,startdate, end date in modal and send them in the following ajax
$.ajax({
url: 'add_events.php',
data: 'title='+ title+'&start='+ start2 +'&end='+ end2,
type: "POST",
success: function(json) {
$( "#getReason" ).modal('hide');
$('#mydiv').hide();
$('body').removeClass('blockMask');//calendar.fullCalendar( 'refetchEvents');
$('#calendar').fullCalendar('refetchEvents');
}
});
in add_events.php save the details in db
In your main page use this method for dynamically creating event source
function eventSourceCall(){
eventSourceCall = [
{
url: 'events.php?status=absent',
backgroundColor: 'red',
borderColor: 'white',
textColor: 'white',
rendering: 'background',
cache: false
}
in events.php perform a select operation and retrieve the event parameters as json encoded objects and return them.
In calendar function eventSources: eventSourceCall, add this line for selecting the event source.
I am trying to update my db with the updated start date of an event I drag to a new day. I have a save button that runs the 'clientEvents' method to return all events to insert/update to the db. My problem is returning the correct event start date when an event that has been dragged to a new day.
I am doing this so far to update the event data:
$('#calendar').fullCalendar({
eventDrop: function(event, delta, element) {
event.title = "NEW TITLE!";
event.start = event.start.format();
event.color = "blue";
console.log(event.start.format());
console.log(event.start);
console.log(event.start._i);
console.log(event.title);
console.log(event.color);
$('#calendar').fullCalendar('updateEvent', event); //update the event data
}
})
and my function to return all the events:
function saveEvents() {
//get events array
var events = $('#calendar').fullCalendar('clientEvents');
console.log(events);
}
However, using this code, the title and color update fine but the start date will not update. Using console.log, the new title and color return correctly, but the start date is the original date, not the new date the event was dragged to. The same for the event data returned in my saveEvents function: the new title and color are correct, but not the start date.
The strange thing is, in the 3 ways I log the event.start, only the event.start.format() shows the correct new date. How do I get the new date saved so when a call 'clientEvents', the correct new data is returned?
Could you explain what you mean by "original date" compared to what should be the "new date" ? Because you don't seem to update your start date as you only modify its format.
Create a moment copy of start, format it and attach to new event. Put your ajax call in the eventDrop callback in initialization of fullCalendar. Example solution using php and mysql:
$('#calendar').fullCalendar({
eventDrop: function(event, delta, element) {
var event_format = event;
event_format.start = moment(event.start).format();
$.ajax({
url : "script.php",
data : event=JSON.stringify(event_format),
type : "POST"
});
}
})
php:
<?php
$event = json_decode($_POST['event']);
$query = "UPDATE x SET start = '$event->start'";
mysql_query($query) or die ($query);
?>
Doing the same for the enddate would be a good idea :)
My fullcalendar duplicates events visually when i drag them to another timeslot. I have simplified my code down to the eventDrop to isolate the issue and yet I'm unable to understand the issue.
If I store the events to my localStorage I don't get a duplicate in the storage and the duplicate disappears when I reload the page. This means the problem is only visual and with Full Calendar itself.
However, this is obviously a huge issue as I don't want to reload the page: I want to stay in the current view changing what I need.
Here's my code for the eventDrop:
eventDrop: function(event, delta, revertFunc, jsEvent, ui, view) {
if (!confirm("Are you sure you want to change " + event.title + " ?")) {
/*If the user cancels the change, the event returns to its original position. Otherwise it saves the event.*/
revertFunc(); /*Revert changes using the predefined function revertFunc of fullCalendar.*/
$("#calendar").fullCalendar("refetchEvents");
/*FullCalendar Method to refetch events from all sources and rerender them on the screen.*/
} else {
updateConfirm(event); /*Use the logic to confirm the event update properties*/
Evento.prototype.Update(event); /*Modify the targeted event on the localStorage using the Update method of the Evento Class*/
$("#calendar").fullCalendar("updateEvent", event);
/*FullCalendar Method to report changes to an event and render them on the calendar.*/
$("#calendar").fullCalendar("refetchEvents");
/*FullCalendar Method to refetch events from all sources and rerender them on the screen.*/
}
}
And here's a gif of the issue:
https://i.imgur.com/rFPvvjE.gif
UPDATE: With slicedtoad's help I isolated the issue to my updateConfirm logic:
var updateConfirm = function(event) {
if (confirm("New title?")) { /*Check if the user wants to change the event title*/
event.title = prompt("Enter the new title: "); /*Set new title for the event*/
} else {
event.title = event.title;
}
if (confirm("New description?")) { /*Check if the user wants to change the event description*/
event.description = prompt("Enter the new description: "); /*Set new description for the event*/
} else {
event.description = event.description;
}
if (confirm("Is the event important?")) { /*Check if the user wants to change the event priority*/
event.overlap = false;
event.backgroundColor = "red"; /*Set new priority for the event*/
} else {
event.overlap = true;
event.backgroundColor = "blue"; /*Set new priority for the event*/
}
};
UPDATE 2:
console.log(event) before updateConfirm(event):
Object {id: "2015-01-27T15:29:11+00:00", title: "título", start: m, end: m, allDay: false…}_allDay: false_end: m_id: "2015-01-27T15:29:11+00:00"_start: mallDay: falsebackgroundColor: "blue"className: Array[0]description: "gt4"end: mid: "2015-01-27T15:29:11+00:00"overlap: truesource: Objectstart: mstoringId: "2015-01-27T15:29:11+00:00"title: "título"__proto__: Object
console.log(event) after updateConfirm(event):
Object {id: "2015-01-27T15:29:11+00:00", title: "título", start: m, end: m, allDay: false…}_allDay: false_end: m_id: "2015-01-27T15:29:11+00:00"_start: mallDay: falsebackgroundColor: "blue"className: Array[0]description: "gt4"end: mid: "2015-01-27T15:29:11+00:00"overlap: truesource: Objectstart: mstoringId: "2015-01-27T15:29:11+00:00"title: "título"__proto__: Object
Since the event is not locally sourced, calling updateEvent isn't necessary since the event will be refetched from the database when you call $("#calendar").fullCalendar("refetchEvents");
I'm not entirely sure why it would duplicate but the event modified by updateEvent seems to persist past the refetch. You must be changing it's ID or replacing it with another event object, but I wasn't able to reproduce it.
So try removing the update line
} else {
updateConfirm(event);
Evento.prototype.Update(event);
$("#calendar").fullCalendar("refetchEvents");
}
If that doesn't work, try deleting the event manually:
$("#calendar").fullCalendar( 'removeEvents', event._id ) //replace the updateEvent call with this
//or event.id if your events have an explicit id
Addendum
You likely want to actually figure out the cause of the problem since the above just patches it. Something in Evento.prototype.Update updateConfirm is modifying the event to the point that FC thinks it is a different event. Is it being copied and replacing itself? Are you playing with it's id?
Do singleton style solution:
This worked for me to stop the duplication which was caused by "new Draggable(container)"
every time reloaded the view
$scope.Draggable = null if ($scope.Draggable == null) {
$scope.Draggable = new Draggable(containerEl, {
itemSelector: '.item',
eventData: function (eventEl) {
return {
title: eventEl.innerText
};
}
});
}