I'm working in week mode and I want to limit visible time range to the earliest event on the week and the latest event on the week.
I guess the right way to solve the problem is to manually filter events that are visible in current week, find the mininum and maximum time and set them to minTime and maxTime property. The problem is that I don't see weekChanged callback (or something like that) which seems a right place to recalculate minTime and maxTime.
This was a lot more work than I expected, hope it works well enough for you. I'm basically doing what I suggested in the comments above.
Edit:
Changed code to improve performance so we don't have to wait for all of the calendar to render before finding out that we need to start all over again.
Note that to do this more effectively we would either have to implement a new callback because none of the existing ones seem to have clientEvents set already or calculate times from the events beforehand (which could get messy in case of timezones).
http://jsfiddle.net/3E8nk/555/
(function(c) {
var startedViewRender = true;
function greaterTime(first, second) {
//Assuming dates are the same year
if (first.clone().dayOfYear(0).isBefore(second.clone().dayOfYear(0)))
return second;
else
return first;
}
var calendarOptions = {
header: {
left: 'prev,next today',
center: 'title',
right: 'agendaWeek'
},
defaultView: 'agendaWeek',
minTime: '00:00:00',
maxTime: '24:00:00',
defaultDate: '2014-06-12',
defaultTimedEventDuration: '02:00:00',
editable: true,
events: [{
title: 'All Day Event',
start: '2014-06-01'
}, {
title: 'Long Event',
start: '2014-06-07',
end: '2014-06-10'
}, {
id: 999,
title: 'Repeating Event',
start: '2014-06-09T16:00:00'
}, {
id: 999,
title: 'Repeating Event',
start: '2014-06-16T16:00:00'
}, {
title: 'Meeting',
start: '2014-06-12T10:30:00',
end: '2014-06-12T12:30:00'
}, {
title: 'Lunch',
start: '2014-06-12T12:00:00'
}, {
title: 'Birthday Party',
start: '2014-06-13T07:00:00'
}, {
title: 'Click for Google',
url: 'http://google.com/',
start: '2014-06-28'
}],
viewRender: function(view) {
startedViewRender = true;
},
eventRender: function(event, element, view) {
if (!startedViewRender)
return;
else
startedViewRender = false;
if (view.name !== 'agendaWeek') {
console.log('not agendaWeek');
return;
}
var events = c.fullCalendar('clientEvents');
if (events.length === 0) {
console.log('no events at all');
//Set to default times?
return;
}
var visibleAndNotAllDayEvents = events.filter(function(event) {
//end not necessarily defined
var endIsWithin = event.end ? event.end.isWithin(view.start, view.end) : false;
return !event.allDay && (event.start.isWithin(view.start, view.end) || endIsWithin);
});
if (visibleAndNotAllDayEvents.length === 0) {
console.log('no visible not all day events');
//Set to default times?
return;
}
var earliest = visibleAndNotAllDayEvents.reduce(function(previousValue, event) {
return greaterTime(previousValue, event.start).isSame(previousValue) ? event.start : previousValue;
}, moment('23:59:59', 'HH:mm:ss'));
var latest = visibleAndNotAllDayEvents.reduce(function(previousValue, event) {
var end = event.end ? event.end.clone() : event.start.clone().add(moment(calendarOptions.defaultTimedEventDuration, 'HH:mm:ss'));
return greaterTime(previousValue, end);
}, moment('00:00:00', 'HH:mm:ss'));
if (calendarOptions.minTime !== earliest.format('HH:mm:ss') || calendarOptions.maxTime !== latest.format('HH:mm:ss')) {
//Reinitialize the whole thing
var currentDate = c.fullCalendar('getDate');
c.fullCalendar('destroy');
c.fullCalendar($.extend(calendarOptions, {
defaultDate: currentDate,
minTime: earliest.format('HH:mm:ss'),
maxTime: latest.format('HH:mm:ss')
}));
}
}
};
c.fullCalendar(calendarOptions);
})($('#calendar'));
You can do that by using eventAfterAllRender, which is executed after all events have been rendered
eventAfterAllRender: function(view) {
var evts = $("#calendar").fullCalendar( 'clientEvents'),
minTime = moment("2014-01-01 23:59:59").format("HH:mm:ss"),
maxTime = moment("2014-01-01 00:00:00").format("HH:mm:ss"),
currentDate = view.calendar.getDate(),
currentMinTime = view.calendar.options.minTime,
currentMaxTime = view.calendar.options.maxTime;
// lets calculate minTime and maxTime based on the week events
// if this event's minTime is 'before' than the minTime, set this as the minTime
for(var i in evts) {
minTime = timeDiff(minTime, evts[i].start.format("HH:mm:ss"), true);
maxTime = timeDiff(maxTime, evts[i].end.format("HH:mm:ss"), false);
}
// If minTime or maxTime don't match, recreate fullcalendar
// This is a pain in the ass : \
// We have to destroy and apply fullcalendar so this can work.
if (minTime != currentMinTime || maxTime != currentMaxTime) {
$("#calendar").fullCalendar('destroy');
$("#calendar").fullCalendar(
$.extend(fcOpts, {
defaultDate: currentDate,
minTime: minTime,
maxTime: maxTime
})
);
}
}
You will have to have a function to calculate which time is the lattest or earliest:
function timeDiff(time1, time2, getMin) {
var d1 = new Date('2014-01-01 ' + time1),
d2 = new Date('2014-01-01 ' + time2);
if (getMin) {
return d1.getTime(d1) - d2.getTime(d2) < 0 ? time1 : time2;
} else {
return d1.getTime(d1) - d2.getTime(d2) > 0 ? time1 : time2;
}
}
See the following JsFiddle for a working example.
Related
Using fullcalendar library, I would like to display the start time for each empty cell on my calendar (empty cells are the one marked with a red cross or red dots in the below screenshot, I modified a bit the aspect of the calendar):
So my expected output is a calendar were timeslots become buttons, when you click you start the process of booking a 30 minutes appointment which would start at the written time (the green slot is an hover effect in the following screenshot):
I can't find any easy way to do it through after reading fullcalendar documentation : https://fullcalendar.io/docs
Subsidiary question, I can't find the way to change the style of the empty cell in the CSS. Can't manage to select the elements through my Chrome console.
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
columnHeaderHtml: function(date) {
if (date.getUTCDay() === 0) {
var date_day = "Lundi";
}
if (date.getUTCDay() === 1) {
var date_day = "Mardi";
}
if (date.getUTCDay() === 2) {
var date_day = "Mercredi";
}
if (date.getUTCDay() === 3) {
var date_day = "Jeudi";
}
if (date.getUTCDay() === 4) {
var date_day = "Vendredi";
}
if (date.getUTCDay() === 5) {
var date_day = "Samedi";
}
if (date.getUTCDay() === 6) {
var date_day = "Dimanche";
}
if(date.getMonth() === 0)
{
var date_month = "Jan";
}
if(date.getMonth() === 1)
{
var date_month = "Fev";
}
if(date.getMonth() === 2)
{
var date_month = "Mar";
}
if(date.getMonth() === 3)
{
var date_month = "Avr";
}
if(date.getMonth() === 4)
{
var date_month = "Mai";
}
if(date.getMonth() === 5)
{
var date_month = "Juin";
}
if(date.getMonth() === 6)
{
var date_month = "Juil";
}
if(date.getMonth() === 7)
{
var date_month = "Août";
}
if(date.getMonth() === 8)
{
var date_month = "Sept";
}
if(date.getMonth() === 9)
{
var date_month = "Oct";
}
if(date.getMonth() === 10)
{
var date_month = "Nov";
}
if(date.getMonth() === 11)
{
var date_month = "Dec";
}
var day_num = date.getDate();
return '<b>'+date_day+'</b><br><small>'+day_num+" "+date_month+"</small>";
},
plugins: [ 'interaction', 'dayGrid', 'list', 'googleCalendar','timeGrid' ],
selectable: true,
defaultView: 'timeGridFourDay',
views: {
timeGridFourDay: {
type: 'timeGrid',
duration: { days: 4 },
buttonText: '4 day'
}
},
slotLabelFormat:{
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'short'
},
locale:'fr',
header: {
left: 'prev today',
right: 'next'
},
validRange: {
start: '2019-08-05',
end: '2019-09-05'
},
allDaySlot:false,
firstDay:1,
minTime:"08:00:00",
maxTime:"20:00:00",
displayEventTime: true, // don't show the time column in list view
// THIS KEY WON'T WORK IN PRODUCTION!!!
// To make your own Google API key, follow the directions here:
// http://fullcalendar.io/docs/google_calendar/
googleCalendarApiKey: 'AIzaSyAL9K2UqkCVfV0n81mDW0iEpOJSwcklfsY',
// US Holidays
events: 'fr.fr#holiday#group.v.calendar.google.com',
eventClick: function(arg) {
arg.jsEvent.preventDefault() // don't navigate in main tab
console.log(arg);
},
select: function(info) {
console.log(info)
},
loading: function(bool) {
},
eventSources: [
{
googleCalendarId: 'contact#vetorino.com',
className: "gcalEvent"
}],
displayEventEnd:false,
events:[
{ // this object will be "parsed" into an Event Object
start: '2019-08-05 12:30:00', // a property!
end: '2019-08-05 14:00:00', // a property! ** see important note below about 'e6d' **
overlap: true,
backgroundColor:"#F7F7F7",
textColor:"#979797",
classNames:"closed",
}],
contentHeight: "auto",
});
calendar.render();
});
So far as shown in my previous screenshot I just managed to have empty cells, the only cells where you find some information are cells containing events.
As discussed in the comments above, there is no single element in the fullCalendar HTML which represents a specific "cell" or "slot" in the timeGrid view. The grid you can see on screen is actually an illusion created by layering multiple tables on top of each other.
So to meet your requirement for a user to be able to select a 20-minute appointment in a free slot, I can see two main options. The first is what I would normally recommend, using the standard fullCalendar functionality. The second is more like what you are asking for, but I think it over-complicates things.
1) This option simply sets the calendar with a slot duration of 20 minutes, and then has code to stop the user from selecting a longer period of time (they cannot select a shorter period, due to the slotDuration setting. This means that they can click on any empty space once and it will know to create an event of the correct length in that location. The user is not allowed to select any slot where an event already exists. (P.S. I expect in reality you will need to collect more data before adding events, but for the demonstration it adds an event instantly.)
document.addEventListener("DOMContentLoaded", function() {
var Calendar = FullCalendar.Calendar;
var calendarEl = document.getElementById("calendar");
var calendar = new Calendar(calendarEl, {
plugins: ["timeGrid", "interaction"],
header: {
left: "prev,next today",
center: "title",
right: "timeGridFourDay"
},
defaultView: "timeGridFourDay",
views: {
timeGridFourDay: {
type: "timeGrid",
duration: { days: 4 },
buttonText: "4 day"
}
},
slotLabelFormat: {
hour: "numeric",
minute: "2-digit",
omitZeroMinute: true,
meridiem: "short"
},
allDaySlot: false,
firstDay: 1,
minTime: "08:00:00",
maxTime: "20:00:00",
contentHeight: "auto",
slotDuration: "00:20:00",
selectable: true,
select: function(info) {
//console.log(info);
calendar.addEvent({ "title": "Test", start: info.start, end: info.end })
calendar.unselect();
},
selectOverlap: false,
selectAllow: function(selectInfo) {
var stM = moment(selectInfo.start);
var enM = moment(selectInfo.end);
var diff = enM.diff(stM, "minutes");
console.log(diff);
if (diff > 20)
{
return false;
}
return true;
},
events: [
{ "title": "Existing event", "start": "2019-08-08 10:00", "end": "2019-08-08 10:20"},
{ "title": "Existing event", "start": "2019-08-08 13:20", "end": "2019-08-08 13:40"},
]
});
calendar.render();
});
Demo: https://codepen.io/ADyson82/pen/aeqJQg
2) This option is closer to your desired UI (from your 2nd screenshot) but is a bit more complicated to achieve. I also, personally, think it leaves your calendar looking cluttered, and making it harder to see where the free and busy slots are, but ultimately it's up to you how you want to implement it. This works by adding a second event source, containing a list of all currently free slots. These are then used to display the start time of each free slot in the centre of it. They are coloured differently from the existing events (indicating a busy slot), so that it's a bit easier to tell the difference.
Of course, this requires you to use your server-side code to calculate all the currently free slots in your database and use that information to populate the second event source. (In the demo the free slot data is static, but of course that will not work in a real application.)
document.addEventListener("DOMContentLoaded", function() {
var Calendar = FullCalendar.Calendar;
var calendarEl = document.getElementById("calendar");
var calendar = new Calendar(calendarEl, {
plugins: ["timeGrid", "interaction"],
header: {
left: "prev,next today",
center: "title",
right: "timeGridFourDay"
},
defaultView: "timeGridFourDay",
views: {
timeGridFourDay: {
type: "timeGrid",
duration: { days: 4 },
buttonText: "4 day"
}
},
slotLabelFormat: {
hour: "numeric",
minute: "2-digit",
omitZeroMinute: true,
meridiem: "short"
},
allDaySlot: false,
firstDay: 1,
minTime: "08:00:00",
maxTime: "20:00:00",
contentHeight: "auto",
slotDuration: "00:20:00",
displayEventTime: false,
eventClick: function(info) {
if (info.event.extendedProps.type == "free") {
calendar.addEvent({
title: "Test",
start: info.event.start,
end: info.event.end
});
info.event.remove(); //delete the "free slot" event
}
},
eventSources: [
{
id: "busy",
events: [
{
title: "Existing event",
start: "2019-08-08 10:00",
end: "2019-08-08 10:20"
},
{
title: "Existing event",
start: "2019-08-08 13:20",
end: "2019-08-08 13:40"
}
]
},
{
id: "free",
backgroundColor: "green",
events: [
{
title: "08:00",
start: "2019-08-08 08:00",
end: "2019-08-08 08:20",
type: "free"
},
{
title: "08:20",
start: "2019-08-08 08:20",
end: "2019-08-08 08:40",
type: "free"
},
{
title: "08:40",
start: "2019-08-08 08:40",
end: "2019-08-08 09:00",
type: "free"
},
{
title: "09:00",
start: "2019-08-08 09:00",
end: "2019-08-08 09:20",
type: "free"
},
{
title: "09:20",
start: "2019-08-08 09:20",
end: "2019-08-08 09:40",
type: "free"
},
{
title: "09:40",
start: "2019-08-08 09:40",
end: "2019-08-08 10:00",
type: "free"
},
{
title: "10:20",
start: "2019-08-08 10:20",
end: "2019-08-08 10:40",
type: "free"
},
{
title: "10:40",
start: "2019-08-08 10:40",
end: "2019-08-08 11:00",
type: "free"
},
]
}
]
});
calendar.render();
});
For this demo I only created handful of the "free" slots (because it was tedious to create them), but hopefully you can get the idea of how it would start to look with dozens of them all over the calendar. Of course again you can amend the CSS to your requirements.
Demo: https://codepen.io/ADyson82/pen/JgpNEX
(You can of course amend the CSS of this further to make it appear more like your desired look and feel.)
Addendum: Here's the OP's final version, for anyone who is interested in the end product - based on taking the above suggestions into consideration: https://codepen.io/hugo-trial/pen/rXdajv
I've got problem with my fullcalendar,but only on Mozilla Firefox browser.I want to add 2 events:
First on date 17.07 to 20.07 another on 18.07 to 22.07.
My browser in result show me first event on date 17.07 without end date and second correct result .I don't know why :/ in another browsers (chrome,opera) it's looks better.
$('.calendar').fullCalendar({
header: {
right: '',
center: '',
left: ''
},
firstDay: 1,
buttonIcons: {
prev: 'calendar__prev',
next: 'calendar__next'
},
theme: false,
selectable: true,
selectHelper: true,
editable: false,
events: [
],
viewRender: function (view) {
var calendarDate = $('.calendar').fullCalendar('getDate');
var calendarMonth = calendarDate.month();
//Set data attribute for header. This is used to switch header images using css
$('.calendar .fc-toolbar').attr('data-calendar-month', calendarMonth);
//Set title in page header
$('.content__title--calendar > h1').html(view.title);
},
eventClick: function (event, element) {
$('#edit-event input[value='+event.className+']').prop('checked', true);
$('#edit-event').modal('show');
$('.edit-event__id').val(event.id);
$('.title').val(event.title);
$('.description').val(event.description);
$('.start_date').val(event.start);
$('.stop_date').val(event.stop);
$('.user_full_name').val(event.author);
},
eventRender: function(event, element) {
startDate=event.start.toISOString().substring(0, 10);
stopDate=event.stop.substring(0, 10);
title = event.title + "<br />"+' [ '+startDate+' ]' + ' [ '+stopDate+' ] ';
element.popover({
title: title,
trigger: 'hover',
placement: 'auto',
container: 'body',
html: true
});
}
});
I've got my data from api and editing it on that function.
function getApiData(daysInMonth){
var api_url = $('input[name="apiUrl"]').val();
api_url = api_url+'/api/eventStart='+y+'-'+m+'-'+'01'+'&eventStop='+y+'-'+m+'-'+daysInMonth;
$.getJSON(api_url,function(result){
$.each(result.data,function(key,index){
bg = event_color(index.category_name)
if(index.category_name == "Holiday"){
title = index.category_name+' - '+index.user_name;
endDate=index.event_stop.substring(0, 10);
endTime=index.event_stop.substring(10, 19);
var datePlus1 = endDate.split('-');
datePlus1[2] = Number(datePlus1[2])+1;
if(datePlus1[2] > daysInMonth){
datePlus1[2] = '01';
datePlus1[1] = Number(datePlus1[1])+1;
if(datePlus1[1]<10){
end=datePlus1[0]+'-0'+datePlus1[1]+'-'+datePlus1[2];
}
else{
end=datePlus1[0]+'-'+datePlus1[1]+'-'+datePlus1[2];
}
}
else{
end=datePlus1[0]+'-'+datePlus1[1]+'-'+datePlus1[2];
}
$('.calendar').fullCalendar('renderEvent', {
id: index.id,
title: title,
start: index.event_start,
end: end,
stop: index.event_stop,
description:index.event_description,
author: index.user_full_name,
allDay: true,
className: bg,
}, true);
}
}
}
`
ok I solved the problem. Function must add '0' to value day if days < 10 becouse the date looks like this 2019-11-1 instead 2019-11-01.
I want to show on the calendar, that what dates are free dates in the year. For these, i want to set a red background.
My problem is, that with this code, it gives the red background to all the dates.
I am using this in the dayRender event.
var unnep_napok =
[
"2019-01-12",
"2019-01-15"
];
$('#calendar').fullCalendar({
events: valami,
lang: 'hu',
dayClick: function(event) {
$(cell).removeClass('ui-widget-content');
$(cell).addClass('holiday');
$(this).css('background-color', 'green');
},
defaultView: 'month',
contentHeight: 'auto',
slotEventOverlap: false,
eventRender: function(eventObj, $el) {
$el.popover({
title: ' ',
content: eventObj.description,
trigger: 'hover',
placement: 'top',
container: 'body'
});
},
dayRender: function (date, cell) {
for(i = 0; i < unnep_napok.length; i++ )
{
cell.css("background-color", "red");
}
}
});
Update with compare:
dayRender: function (date, cell) {
for(i = 0; i < unnep_napok.length; i++ )
{
if(date == unnep_napok[i] )
{
cell.css("background-color", "red");
}
}
}
Update 2, formatting array elements:
dayRender: function (date, cell)
{
for(i = 0; i < unnep_napok.length; i++ )
{
var datum = unnep_napok[i].moment.format('yyyy-mm-dd');
if(date.getDate() == datum )
{
cell.css("background-color", "red");
}
}
}
Following your update, there are still some problems which could be resolved by reading the documentation (and my earlier comments) more carefully:
1) I didn't give you the literal values to use in the "format" command. Did you read the documentation fully? As you can see, the correct format would be YYYY-MM-DD (big letters not small letters).
2) unnep_napok[i].moment.format ...this is not how you create a momentJS object. I would expect your browser gave an error in the console about this.
3) But anyway 2) is not important really, because as I mentioned in my last comment, it's the date value which you need to format ... your unnep_napok values are already strings!!
4) date.getDate() .. I don't know where you got this from?? MomentJS does not document any such function.
This should work for you:
dayRender: function (date, cell)
{
for(i = 0; i < unnep_napok.length; i++ )
{
if(date.format('YYYY-MM-DD') == unnep_napok[i])
{
cell.css("background-color", "red");
}
}
}
A running example based on ADyson's answer. I have also covered popover example for a quick start.
.popover works this way $(element).popover and doesn't work using element.popover
Running example: https://jsfiddle.net/alifaraze/mr53d7nz/8/
HTML
<div id="calendar"></div>
Script
$(document).ready(function() {
var unnep_napok = [
"2019-01-23",
"2019-01-25"
];
$('#calendar').fullCalendar({
events: [{
id: 1,
title: 'Full Day Event',
start: '2019-01-02',
end: '2019-01-03',
description: 'A full day event description'
},
{
id: 2,
title: 'Whole Week Event',
start: '2019-01-06',
end: '2019-01-10',
description: 'Whole week event description'
}
// more events here
],
eventRender: function(event, element) {
$(element).popover({
title: function() {
return "<B>" + event.title + "</B>";
},
placement: 'auto',
html: true,
trigger: 'click',
animation: 'false',
content: function() {
return "<h4>"+ event.description+"</h4>"
},
container: 'body'
});
},
dayRender: function(date, cell) {
for (i = 0; i < unnep_napok.length; i++) {
if (date.format('YYYY-MM-DD') == unnep_napok[i]) {
cell.css("background-color", "red");
}
}
}
});
})
In my project, users can book rooms. My rooms have disponibility hours (eg 08:00-17:00). I try to use Business Hours but disponibilities change in summer and winter.
I also tried to use inverse-background events with date ranges like this post but if I use selectConstraint, the range is not taken in account.
The best would be to add date range to business hours, but it seems not implemented yet.
Has anyone a solution for my needs?
Thanks
edit: here is my fullcalendar options
function FCInit(){
var formatColumn, formatColumnWeek;
// Entete des colonnes
if ($(window).width() < 600) {
formatColumn = 'ddd';
formatColumnWeek = 'ddd\nDD/MM';
}
else {
formatColumn = 'dddd';
formatColumnWeek = 'dddd\nDD/MM';
}
var fcOpts = {
header: {
left: 'today,datePickerButton',
center: 'prev,title,next',
right: 'month,agendaWeek,agendaDay'
},
contentHeight: 'auto',
eventLimit: false,
allDaySlot: true,
slotEventOverlap: false,
nowIndicator: true,
timeFormat: 'H:mm',
columnFormat: formatColumn, // Format des jours dans l'entete ddd: Mon / ddd M/D : Mon 09/07 / dddd : MOnday /
navLinks: true,
eventOverlap: false,
selectable: true,
selectHelper: true,
selectOverlap: true,
selectConstraint:999,
unselectCancel: '#reservation',
views: {
week: {
columnFormat: formatColumnWeek
}
},
events:[{
id:3,
title:"R\u00e9serv\u00e9",
start:"2017-11-02 08:00",
end:"2017-11-02 10:00",
overlap:false,
color:"#C41305"
},{
id:999,
className:"fc-nonbusiness",
title:"",
start:"08:00",
end:"17:00",
dow:[4],
ranges:[
{
start:"2017-11-01",
end:"2017-11-30"
}
],
rendering:"inverse-background",
}],
/* Ajout de datepicker (nécessite Jquery UI css et js) */
customButtons: {
datePickerButton: {
text: '',
click: function () {
var $btnCustom = $('.fc-datePickerButton-button'); // name of custom button in the generated code
$btnCustom.after('<input type="hidden" id="hiddenDate" class="datepicker"/>');
$("#hiddenDate").datepicker({
flat: true,
showOn: "button",
dateFormat: "yy-mm-dd",
onSelect: function (dateText, inst) {
$('#full-calendar').fullCalendar('changeView', 'agendaDay', dateText);
}
});
var $btnDatepicker = $(".ui-datepicker-trigger"); // name of the generated datepicker UI
//Below are required for manipulating dynamically created datepicker on custom button click
$("#hiddenDate").show().focus().hide();
$btnDatepicker.trigger("click"); //dynamically generated button for datepicker when clicked on input textbox
$btnDatepicker.hide();
$btnDatepicker.remove();
$("input.datepicker").not(":first").remove();//dynamically appended every time on custom button click
}
}
},
dayRender: function(date, cell){
if(date.isBefore(new Date())){
cell.css('cursor','no-allowed');
}
},
eventRender: function (event, element) {
if(event.ranges) {
return (event.ranges.filter(function (range) { // test event against all the ranges
return (event.start.isBefore(range.end) &&
event.end.isAfter(range.start));
}).length) > 0;
}
if(event.rendering === "background"){
// Just add some text or html to the event element.
element.append("<div class='fc-title'>"+event.title+"</div>");
}
},
dayClick: function(date, jsEvent, view){
if(date.isSameOrAfter(new Date()) && view.name === 'month'){
$('#full-calendar').fullCalendar('changeView', 'agendaWeek', date);
}
},
select: function(start, end, jsEvent, view){
if(start.isSameOrAfter(new Date()) && view.name !== 'month'){
$('#reservation_dateFrom').val(start.format('DD/MM/YYYY HH:mm'));
$('#reservation_dateTo').val(end.format('DD/MM/YYYY HH:mm'));
$('#reservation').modal('show');
}else if(start.isBefore(new Date())){
alert('Il n\'est pas possible de réserver dans le passé');
$('#full-calendar').fullCalendar('unselect');
}
}
};
$('#full-calendar').fullCalendar(fcOpts);
};
and my symfony entities for storing the datas (where Horaire is a collection of business hours):
/*src/AppBundle/Entity/HoraireSalle.php*/
class HoraireSalle
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="dateFrom", type="datetime")
*/
private $dateFrom;
/**
* #var \DateTime
*
* #ORM\Column(name="dateTo", type="datetime")
*/
private $dateTo;
/**
* #ORM\ManyToOne(targetEntity="Horaire", inversedBy="salles")
*/
private $horaire;
/**
* #ORM\ManyToOne(targetEntity="Salle", inversedBy="horaires")
*/
private $salle;
...
}
Thanks to #ADyson I do more or less what I want. Here is my solution.
function isAllowed(start, end) {
var events = $('#full-calendar').fullCalendar('clientEvents', function (event) {
return event.rendering === 'inverse-background' && event.start && event.end;
});
var allow = events.filter(function (event) {
return (start.isBetween(moment(new Date(event.ranges[0].start)), moment(new Date(event.ranges[0].end)))
&& end.isBetween(moment(new Date(event.ranges[0].start)), moment(new Date(event.ranges[0].end)))
&& start.format("HH:mm") >= event.start.format("HH:mm") && end.format("HH:mm") <= event.end.format("HH:mm")
&& event.dow.indexOf(start.day()) > -1
&& event.dow.indexOf(end.day()) > -1)
});
events = $('#full-calendar').fullCalendar('clientEvents', function (event) {
return event.rendering !== 'inverse-background' && event.start && event.end;
});
var overlap = events.filter(function (event) {
return event.start.isBefore(end) && event.end.isAfter(start);
});
if (allow.length && overlap.length == 0) {
return true;
}
return false;
}
function FCInit() {
var formatColumn, formatColumnWeek;
if ($(window).width() < 600) {
formatColumn = 'ddd';
formatColumnWeek = 'ddd\nDD/MM';
}
else {
formatColumn = 'dddd';
formatColumnWeek = 'dddd\nDD/MM';
}
var fcOpts = {
header: { // Ordre des boutons de l'entete
left: 'today,datePickerButton',
center: 'prev,title,next',
right: 'month,agendaWeek,agendaDay'
},
contentHeight: 'auto',
eventLimit: false,
allDaySlot: true,
slotEventOverlap: false,
nowIndicator: true,
timeFormat: 'H:mm',
columnFormat: formatColumn,
navLinks: true,
eventOverlap: false,
selectable: true,
selectHelper: true,
{% if businessHours is defined and businessHours is not empty %}
selectAllow: function (eventInfo) {
return isAllowed(eventInfo.start, eventInfo.end);
},
{% else %}
selectOverlap: false,
{% endif %}
unselectCancel: '#reservation',
views: {
week: {
columnFormat: formatColumnWeek
}
},
events: [{
id:3,
title:"R\u00e9serv\u00e9",
start:"2017-11-02 08:00",
end:"2017-11-02 10:00",
overlap:false,
color:"#C41305"
},{
id:999,
className:"fc-nonbusiness",
title:"",
start:"08:00",
end:"17:00",
dow:[4],
ranges:[
{
start:"2017-11-01",
end:"2017-11-30"
}
],
rendering:"inverse-background",
}],
/* Ajout de datepicker (nécessite Jquery UI css et js) */
customButtons: {
datePickerButton: {
text: '',
click: function () {
var $btnCustom = $('.fc-datePickerButton-button'); // name of custom button in the generated code
$btnCustom.after('<input type="hidden" id="hiddenDate" class="datepicker"/>');
$("#hiddenDate").datepicker({
flat: true,
showOn: "button",
dateFormat: "yy-mm-dd",
onSelect: function (dateText, inst) {
$('#full-calendar').fullCalendar('changeView', 'agendaDay', dateText);
}
});
var $btnDatepicker = $(".ui-datepicker-trigger"); // name of the generated datepicker UI
//Below are required for manipulating dynamically created datepicker on custom button click
$("#hiddenDate").show().focus().hide();
$btnDatepicker.trigger("click"); //dynamically generated button for datepicker when clicked on input textbox
$btnDatepicker.hide();
$btnDatepicker.remove();
$("input.datepicker").not(":first").remove();//dynamically appended every time on custom button click
}
}
},
dayRender: function (date, cell) {
if (date.isBefore(new Date())) {
cell.css('cursor', 'no-allowed');
}
},
eventRender: function (event, element, view) {
if (event.rendering === 'inverse-background' && event.ranges) {
return (event.ranges.filter(function (range) { // test event against all the ranges
var start = moment(new Date(range.start));
var end = moment(new Date(range.end));
return (view.start.isSameOrBefore(end) &&
view.end.isSameOrAfter(start)) &&
view.start.day(event.dow[0]).isBetween(start, end);
}).length > 0);
}
if (event.rendering === "background") {
// Just add some text or html to the event element.
$(element).data("title",event.title);
}
},
dayClick: function (date, jsEvent, view) {
if (date.isSameOrAfter(new Date()) && view.name === 'month') {
$('#full-calendar').fullCalendar('changeView', 'agendaWeek', date);
}
},
select: function (start, end, jsEvent, view) {
if (start.isSameOrAfter(new Date()) && view.name !== 'month') {
$('#reservation_dateFrom').val(start.format('DD/MM/YYYY HH:mm'));
$('#reservation_dateTo').val(end.format('DD/MM/YYYY HH:mm'));
$('#reservation').modal('show');
} else if (start.isBefore(new Date())) {
alert('Il n\'est pas possible de réserver dans le passé');
$('#full-calendar').fullCalendar('unselect');
}
}
};
$('#full-calendar').fullCalendar(fcOpts);
working example fullcalendar dynamic dow range
Suppose that you need to requiring event in between following date range
start: "2018-06-01",
end: "2018-08-01"
http://jsfiddle.net/521wucLq/
I'm building a grade school, I have a bunch of disciplines and it's day of the week. I need to insert these disciplines into the calendar. The problem is:
There is no way to add only by the day of the week. See:
Today is Sunday( 04/09/2016 ). But the calendar days are from the PAST week.
So, if you have:
var day = 'monday';
When you are going to insert an event in runtime you'd do like so:
$('#calendario').fullCalendar(
'renderEvent',
{
id: id
title: $(this).find('a').text(),
start: year+ '-' +month+ '-' +day+ 'T' + startTime,
end: year+ '-' +month+ '-' +day+ 'T' + endTime,
backgroundColor: color,
className: someClass
}
)
As you can see, I HAVE TO specify dayof the month.
The problem is: Even using weekView, I can't work with events using only week days... I have to give the entire date(YYYY-MM-DD).
Today is sunday(04/09/2016). The calendar is half last month, half current month. How am I supposed to insert an event only by the day of the week ?
Also tried working with momentjs.
moment().day(1).hours(10).minutes(0).format('YYYY-MM-DD')
moment will return the second day of the week (0 - sunday, 1 - monday ), wich is 05 (currently week) and not the same as fullCalendar 29(last week). So I have no Idea how to insert my disciplines/events into the calendar.
You can provide events as a function and loop through the time period currently displayed, creating events on specific days
https://jsfiddle.net/0roafa2f/
$('#calendar').fullCalendar({
events: dowEvents
});
function dowEvents(start, end, tz, callback) {
var events = [];
var curr = start;
while(curr <= end) {
if(curr.format('dddd') === 'Monday') {
events.push({
title: 'Monday event',
start: moment(curr)
});
}
curr = curr.add(1, 'day');
}
callback(events);
}
According to the source code of the demo on this page:
https://fullcalendar.io/
meanwhile, events is an array object that can be listed manually, fetched from a url with json response or fetched from a function as stated here: https://fullcalendar.io/docs/event-data
$(function() {
var todayDate = moment().startOf('day');
var YM = todayDate.format('YYYY-MM');
var YESTERDAY = todayDate.clone().subtract(1, 'day').format('YYYY-MM-DD');
var TODAY = todayDate.format('YYYY-MM-DD');
var TOMORROW = todayDate.clone().add(1, 'day').format('YYYY-MM-DD');
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay,listWeek'
},
editable: true,
eventLimit: true, // allow "more" link when too many events
navLinks: true,
events: [
{
title: 'All Day Event',
start: YM + '-01'
},
{
title: 'Long Event',
start: YM + '-07',
end: YM + '-10'
},
{
id: 999,
title: 'Repeating Event',
start: YM + '-09T16:00:00'
},
{
id: 999,
title: 'Repeating Event',
start: YM + '-16T16:00:00'
},
{
title: 'Conference',
start: YESTERDAY,
end: TOMORROW
},
{
title: 'Meeting',
start: TODAY + 'T10:30:00',
end: TODAY + 'T12:30:00'
},
{
title: 'Lunch',
start: TODAY + 'T12:00:00'
},
{
title: 'Meeting',
start: TODAY + 'T14:30:00'
},
{
title: 'Happy Hour',
start: TODAY + 'T17:30:00'
},
{
title: 'Dinner',
start: TODAY + 'T20:00:00'
},
{
title: 'Birthday Party',
start: TOMORROW + 'T07:00:00'
},
{
title: 'Click for Google',
url: 'http://google.com/',
start: YM + '-28'
}
]
});
});
As far i understand if you want the event to be based on days like(sun,mon,tues) etc you can use dow it accepts array [0,1,2,3,4,5,6] sun,mon,tues respectively. Use start:"00:00:00" if you want it to repeat every day. e.g
Try this
events.push({
start: '00:00:00',
title: 'recursive event',
dow: [0,1,2] // each sun,mon,tues
})