How to prevent event overlapping when updating existing events in Full Calendar? - javascript

I have used eventOverlap: false, and selectOverlap: false, to prevent the user from overlapping events. However, I am trying to prevent the user from overlapping existing events.
In my full calendar the user can click on an event, which opens a pop up dialog and allows the user to update the date/time of the selected event. However, the user is able choose a date/time where an event is already booked. Therefore, I want to have a validation on the Save button that checks if the updated date/time has an event or not before any changes are made. These two screen shots show this problem graphically.
1. Shows that event time is being updated. 2. Shows the event is overlapping after it has been updated
var events = []; //global array where all the events are stored
function FetchEventAndRenderCalendar() {
//fetch info from database and add it to the events array
events = [];
$.ajax({
type: "GET",
url: "/SessionScheduler/GetEvents",
success: function (data) {
$.each(data, function (i, v) {
events.push({
id: v.Id,
title: v.Title,
description: v.Description,
start: moment(v.StartDate),
end: moment(v.EndDate),
tutorName: v.TutorName,
color: v.ThemeColour
});
})
//then display the calendar with the events
GenerateCalender(events);
},
error: function (error) {
alert('failed');
}
})
}
This is the Save button where I want to have a validation check. I have looked at this solution but this didn't work for me
$('#btnSave').click(function () {
//validation
var selectedStartDate = moment(document.getElementById('txtStart').value.trim(), "DD/MM/YYYY HH:mm a").toDate();
var selectedEndDate = moment(document.getElementById('txtEnd').value.trim(), "DD/MM/YYYY HH:mm a").toDate();
if (selectedStartDate > selectedEndDate) {
alert('Invalid end date');
return;
}
if (selectedStartDate.getTime() == selectedEndDate.getTime()) {
alert('Start/End dates can not be the same');
return;
}
var data = {
Id: $('#hdEventID').val(),
Title: $('#txtTitle').val(),
StartDate: $('#txtStart').val(),
EndDate: $('#txtEnd').val(),
Description: $('#txtDescription').val(),
TutorName: $('#txtTutorName').val(),
ThemeColour: $('#ddThemeColour').val()
}
SaveEvent(data);
})
SaveEvent function: Which saves the data
function SaveEvent(data) {
if (selectedEvent != null && confirm("Are you sure?")) {
$.ajax({
type: "POST",
url: '/SessionScheduler/SaveEvent',
data: data,
success: function (data) {
if (data.status) {
//refresh the calendar if the status is true else its failed
FetchEventAndRenderCalendar();
$('#myModalSave').modal('hide'); //hide modal dialog pop window
}
},
error: function () {
alert('Failed');
}
})
}
}

This function will check whether the event passed in overlaps with any other events currently displayed on the calendar.
Note this relies on the events having unique id properties, so it doesn't check itself. It also cannot, by its nature, check any events not currently displayed on the calendar, because fullCalendar doesn't return those from its clientEvents method. You should check again on the server-side before accepting the modification into your database.
//check whether or not the calendar event passed in overlaps with an existing event in the current (client-side) calendar data
//the first parameter should be the event which is being tested
//the second parameter should be a jQuery object wrapping the calendar HTML element
function isCalendarEventOverlapping(event)
{
var evts = cal.fullCalendar('clientEvents');
for (i in evts)
{
if (evts[i].id != event.id)
{
if (event.start.isBefore(evts[i].end) && event.end.isAfter(evts[i].start))
{
return true;
}
}
}
return false;
}

I did some search about this problem.
FullCalender check if selection days has an event?
How to check event is already exist for a day - fullcalendar
How to avoid events duplication on fullcalendar?
Can I prevent events with conflict time?
Everytime, they get all the events from the FC memory, and iterate over them, for searching conflict time.
Unfortunately, there is no simple solution for that.
My suggestions:
You should to make a ajax call before every modification, where your server checks the conflict (if you store the events on the server side)
If your server doesn't store your events, then you have to iterate all the events in the client side, in order to find a conflict.

Related

How do you Get and Add events to a specific source?

I was successfully able to specify multiple sources: My SQL server source, and an empty array to hold any user-generated events userAddedEvents = []. The idea was that I could use this array later to "save" new events to my server.
var userAddedEvents = []; //container for event objects that will hold user-generated content
var calendar = new FullCalendar.Calendar(calendarEl, {
eventSources: [
{
events: function (fetchInfo, successCallback, failureCallback) {
$.ajax({
url: url,
type: 'post',
contentType: "application/json; charset=utf-8", //include fetchInfo in request body instead of form-data
dataType: "json",
data: JSON.stringify(fetchInfo), //the fetchInfo object must be stringified
success: function (data) {
events = $.merge([], data.events);
successCallback(events);
}
});
}
}, //end server source
{
id: 'userAddedEvents',
events: userAddedEvents
} //end local array source
]
});
So, here's me trying to add an event to my userAddedEvents source...
select: function (info) {
// https://fullcalendar.io/docs/select-callback
console.log(info)
console.log('selected ' + info.startStr + ' to ' + info.endStr)
calendar.addEvent({
start: info.startStr,
end: info.endStr,
rendering: 'background', //v4 and v5 use different terminology
color: '#ff9f89',
selected: true, //custom, extended property
}, userAddedEvents);
calendar.unselect(); //clear the current selection
},
Anyway, long story short... when I try to dump out the results of userAddedEvents, it's empty, although, I do see new content on my calendar.
==UPDATED== with example included... I added a custom button where I attempt to get content from the userAddedEvents array.
Alternatively (also shown in the example), I've had success getting ALL events with calendar.getEvents(), then using $.grep to filter some specific property or extended property. Ultimately though, I suppose I'm trying to use a "temporary event source" for the sake of convenience -- I can act upon the userAddedEvents array, stringify it, empty it, etc. I do not know how to getEvents for a specific source object.
customButtons: {
getUserCreatedEvents: {
text: 'Get User Events',
click: function () {
console.log(calendar.getEventSources()); //both event sources are listed
console.log(calendar.getEventSourceById(userAddedEvents)); //missing the id in output {calendar, id, internalEventSource, url}
console.log(calendar.getEventSourceById('userAddedEvents')); //has the the id in output {calendar, id, internalEventSource, url}
console.log(userAddedEvents) //the array is empty
/*
events = calendar.getEvents();
// console.log(events)
var filteredResultsGREP = $.grep(events, function (event) {
return event.rendering === 'background';
});
*/
/*
https://fullcalendar.io/docs/Event-toPlainObject (version 5 only)
*/
// this WILL show any events added based on the property specified
// console.log(filteredResultsGREP);
}
}
}
How do I get the events that are new? I want to hold all user-created events before I send them to SQL for processing.
Firstly, apologies for totally forgetting about this question, having started to help with it.
After some digging it looks like you can't get the raw events for an individual event source, which is a bit annoying.
So I think actually your simplest approach is just to add the events to your separate array, without worrying about the structure in fullCalendar. Then you can just send that list to the server when you want to save them.
select: function (info) {
var evt = {
start: info.startStr,
end: info.endStr,
rendering: "background", //v4 and v5 use different terminology
color: "#ff9f89",
selected: true //custom, extended property
};
calendar.addEvent(evt);
userAddedEvents.push(evt);
calendar.unselect(); //clear the current selection
},
Demo: https://codepen.io/ADyson82/pen/abdVVNM
The only extra complication you might have is if you allow events to be dragged or resized (or otherwise edited) after they've been added, you'll have to sync that with the separate array, which is a bit of extra work.

Add another event source FullCalendar?

I try to add another event source on my FullCalendar. First, I get all events from manual function that I create own and I render all events using renderEvent function. But, I got error when I try to add another event source. So, I want to add holidays feature. I create another event source to get holidays date. That event was success to showed in my FullCalendar. But there is weird thing.
Example : If I have range of my holiday date, such as start : 2019/02/26 - end : 2019/02/28. The event content length doesn't 26 until 28, but start from 26 until 27.
What it's wrong? This is my code for to get all events.
// Get Agenda Data
function getEvents(datas) {
$("input[name=unitAgenda]").attr("value",
$("select[name=unit]").val());
var reUnit = datas.replace("- ","");
$('.calendar').fullCalendar('removeEvents');
$('.calendar').fullCalendar('refetchEvents');
$.ajax({
url: 'agendakerja/kalender/get_events',
dataType: 'JSON',
data: { unit: reUnit },
success: function(data) {
$.each(data["events"], function (index, event) {
$('.calendar').fullCalendar('renderEvent', event, true);
});
}
});
//passing unit name to function
global = reUnit;
}
And this is my code for get holidays date.
eventSources: [
{ url: 'agendakerja/kalender/get_holidays' }
]
Please help me!

My code for Full Calendar works in Chrome, Firefox but why is this breaking in Safari?

This code works in Chrome and Firefox, but on Safari the calendar does not even load on the screen.
Basically I need to pull event data automatically from another website, and then feed it into Full Calendar. The format that the other website sends event details is an array with properties "name" and "time." Full Calendar takes event details in the form of an array called "events" with properties "title" and "start." So with the code below, I tried to rename the array and the properties to follow Full Calendar's format. And then inside the code rendering the calendar, I call that array "events". It works in Chrome and Firefox, but on Safari it does not and the console is telling me that it is expecting a ":" after property name "events"? How can I get this to work with Safari?
var getEvents = function() {
$.ajax({
type: "GET",
url: API_ENDPOINT,
dataType: 'jsonp',
success: displayEvents
});
};
var displayEvents = function(data) {
var events = data.results;
function changeData(events) {
var start;
for (var i=0; i < events.length; i++) {
if (events[i].hasOwnProperty("name")) {
events[i]["title"] = events[i]["name"];
delete events[i]["name"];
}
if (events[i].hasOwnProperty("time")) {
events[i]["start"] = events[i]["time"];
delete events[i]["time"];
}
if (events[i].hasOwnProperty("start")) {
start = events[i].start;
events[i].start = new Date(start);
}
}
}
changeData(events);
console.log(events);
$('#calendar').fullCalendar({
events
});
}
getEvents();
Just change $('#calendar').fullCalendar({ events });
to $('#calendar').fullCalendar({ events: events });
where the first events is the property name of the Object you're passing to the $.fullCalendar() function, and the second events is the value, ie your previously defined variable.

jquery Select2 prevent selecting in ajax response

I want to prevent from adding a category to the Select2 element if it fails creating the row first in my db. The action is not prevented when i call ev.preventDefault(); Nothing happens.. what is wrong?
$('#sel2').select2({
placeholder: 'Enter categories',
minimumInputLength: 3,
multiple: true,
ajax: {
url: 'async/get_categories.php',
dataType: 'json',
quietMillis: 250,
data: function (term, page) {
return {
q: term,
};
},
results: function (data, page) {
return {
results: data.items
};
},
cache: true
},
formatResult: format,
formatSelection: format
}).on('select2-selecting', function(e) {
console.log(e);
if (e.val == 4) {
// if category id equals 4
// do not add this category to select 2
// e.preventDefault();
// the above works just fine and its just for testing
}
// Is something wrong here?
var ev = e;
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: ev.val
},
success: function(response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
ev.preventDefault();
// the above is not working
}
},
error: function() {
alert('Failed to assign category!');
}
});
});
The AJAX request is made asynchronusly, so by the time it has finished the element has already been added. Even though you are calling ev.preventDefault(), it is too late for it to make a difference. So this leaves you with two options:
Make the request synchronusly, which will allow preventDefault to make the difference.
Make the request asynchronusly, and manually remove the element if it fails.
Both options have their pros and cons, and it's up to you to decide which option you go with.
Making the request synchronusly
Pros
The value will never be added if the request fails.
Works well in cases where the element cannot be added quite often.
Cons
Blocks the UI - So the user is potentially left with an unresponsive page while the request is made.
Making the request asynchronusly
Pros
Does not block the UI.
Works well in cases where elements typically can be added.
Cons
The value will always show up for the user, even if it fails later.
You must manually unset the new option.
What's important to consider here is the user experience of both options. When making synchronus requests, it's not uncommon for the browser to stop relaying events - which gives the illusion that the UI has locked up and the page has gone unresponsive. This has the benefit of ensuring that the value never shows up if it isn't allowed. But if users typically can add the elements, it also has the downside of complicating the most common use case.
If users can usually add elements, then it is a better experience to add the element while the request is being made, and then notifying the user later (while removing the element) if there was an issue. This is very common is web applications, and you can see it being used in many places, such as the Twitter and Facebook like buttons (where requests usually work), as well as places on Stack Overflow.
There is a way to get around this with version4 of the select2 library.
on select2:selecting we cancel the preTrigger event. Which will stop the select2:select event. We do our ajax call. On success we then get out Select2 instance then call the trigger of the Observer that way it by passes overwritten trigger method on your select2 instance.
The call method needs your select2 instance as the context so that the existing listeners are available to call.
var sel = $('#sel');
sel.select2(config);
sel.on('select2:selecting', onSelecting);
function onSelecting(event)
{
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: event.params.args.data.id
},
success: function(event, response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
// get select2 instance
var Select2 = $users.data('select2');
// remove prevented flag
delete event.params.args.prevented;
// Call trigger on the observer with select2 instance as context
Select2.constructor.__super__.trigger.call(Select2, 'select', event.params.args);
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
}
}.bind(null, event),
error: function() {
alert('Failed to assign category!');
}
});
event.preventDefault();
return false;
}
here how I did it for yii2 Select2 integrated into Gridview:
'pluginEvents' => [
'select2:selecting' => "
function(event)
{
var select2 = $('#types-" . $model->id . "');
select2.select2('close');
$.post('update',{id: " . $model->id . ", type_id: event.params.args.data.id})
.done (function(response)
{
select2.val(event.params.args.data.id);
select2.trigger('change');
})
.fail(function(response)
{
krajeeDialog.alert('Error on update:'+response.responseText);
});
event.preventDefault();
return false;
}",
],
it allows to asynchoronous update data in the grid using select2 and ajax and return it to previous value if there was an error on updating.

Need to clear a function memory. jQuery running function too many times

Initially, I had a problem that a click event was firing multiple times, but I have managed to overcome that with a probably over use of unbind() and one() as you'll see in my code below!
What I have here is some code which opens up a universally usable Modal window which I use for various things, including, in some cases a password form.
I don't think you need the HTML so I won't post that.
When a button, or an action causes the window to be required, I call the function like this:
showModalAlert(type, theWidth, theHeight, title, html, confirmThis, denyThis)
The first three variables determine how the window will look, title and html determine the content and confirmThis and denyThis are functions set immediately prior to calling this function and determine what the action should be if this is a confirm window and the confirm or deny buttons are press.
In the case of a security window, the confirm button is replace by a "sign it" button which submits a simple password form and returns a User Id from database. If a User Id is successfully returned, the script programatically presses the confirm button and in turn runs it's function as per the call to the inital opening of the modal window.
My problem is that if an incorrect password is entered, or a user cancels the window and then later without refreshing the browser window, re-enters the password correctly, the confirmThis() function is performed twice (or as many times as the incorrect password/cancel action was performed).
So, clearly, what it is doing is "remembering" the confirmThis function each time.
As I said, initially, the password success function was clicking confirmIt twice, copious use of one() has fixed this, it is now definitely only clicking confirmIt once, but it is still performing the function multiple time.
How can I clear this function and ensure it is only performed once?
The function from which I am calling the modal window looks like this:
$('#saveDelivery').click(function () {
function confirmIt() {
formData = (JSON.stringify($('#delDetail').serializeObject()));
saveData(formData);
$('#saveDelivery').removeClass('centreLoader');
};
showModalAlert('security', '300px', '185px', 'Security!', 'You need to "Sign" this action.', confirmIt, '');
});
It's simply a click on the saveDelivery element, the confirmThis function is declared at this point and submits an AJAX form
the actual showModalAlert function is below:
function showModalAlert(type, theWidth, theHeight, title, html, confirmThis, denyThis) {
// stuff that opens the alert window \\
if (confirmThis == '') {
$('#confirmIt').one('click', function () { $('#closeAlert').one('click').click(); });
} else {
$('#confirmIt').one('click', function () { confirmThis(); $('#closeAlert').one('click').click(); });
};
if (denyThis == '') {
$('#denyIt').one('click', function () { $('#closeAlert').one('click').click(); $('#signIt').unbind(); });
} else {
$('#denyIt').one('click', function () { denyThis(); $('#closeAlert').one('click').click(); $('#signIt').unbind(); });
};
if (type == "confirm") {
$('.closeAlert, .signItForm').hide();
};
if (type == "alert") {
$('.alertConfirm, .signItForm').hide();
};
if (type == "fixedAlert") {
$('.closeAlert, .alertConfirm, .signItForm').hide();
};
if (type == "security") {
$('.signItForm').show();
$('.closeAlert').hide();
$('#confirmIt').hide();
$('#signIt').unbind().fadeTo('fast',1);
};
};
$('#signIt').live('click', function () {
var formData = (JSON.stringify($('.secureSign').serializeObject()));
var signitPwd = $('#signItpwd').val();
var jsonURL = "/jsonout/getdata.aspx?sql=SELECT id, password FROM users WHERE password ='" + signitPwd + "' LIMIT 1&output=json&usedb=new&labelName=any&fileName=";
$.getJSON(jsonURL, function (data) {
if (data.length > 0) {
$('.savingUserID').val(data[0].id);
$('#confirmIt').one('click').click();
$('#signIt').fadeTo('fast', 0);
$('#confirmIt').show();
} else {
$('#signIt').fadeTo('fast', 0);
$('#confirmIt').one('click').show();
$('.closeAlert').show();
$('.alertConfirm, .signItForm').hide();
$('#alertTitle').html("Error!");
$('#alertContent').css({ 'text-align': 'center' }).html("Password Denied");
};
});
});
From my understanding of $.one, it merely runs the event ONCE. If you bind it twice to the event, it will run twice instantaneously, but no more.
Example: http://jsfiddle.net/qCwMH/ (click the button, and it will run the event 4 times).
Each time you click saveDelivery, you are infact, binding another $.one event to #confirmIt.
What you could do is unbind your events from confirmIt and denyIt at the start of the modal function (i.e. $('#confirmIt, #denyIt').unbind('click');, and then you will assign them fresh each time that function is called, rather than building on top of them. Not ideal, as binding/unbinding uses more resources than other options, but just give that a try to start with perhaps?

Categories

Resources