Reset depth on Kendo datepicker - javascript

My client has requested that our Kendo DatePicker reset to the original view depth each time the calendar is opened.
Example: the calendar is set to start at "year" view and the user exits from "century" view without choosing a date. The next time the calendar opens it is back at "year" view rather than still being stuck on "century".
After digging through the api this function doesn't seem to be readily exposed.
Has anyone managed to do something similar?

I know this is an old question, but I ran into the same issue and found a solution.
You can call setOptions and pass the options you want to set, such as the depth or start. This will reset the depth allowing you to reset the depth when the calendar is opened. I've added that call to the close event so that it'll occur each time the calendar is closed.
Dojo Demo
var kendoOptions = {
depth: "year",
start: "year"
};
$("#datepicker").kendoDatePicker(kendoOptions);
var datepicker = $("#datepicker").data("kendoDatePicker");
datepicker.bind('close', function() {
// add if statement for scenearios where you want to reset the depth
this.setOptions(kendoOptions);
});

Related

FullCalendar 5 how to modify events created by rrule

I am struggling displaying the correct data from my database with FullCalendar. The database structure I've adopted is to store rrules as ReccurringEvent database objects, and individual deviations from those rrules as separate IndividualEvent objects, all of which have a "Done" property. The goal is to display each event's "Done" status individually even if they're all part of the same RRule.
ReccurringEvents are set up in FullCalendar as:
calendar.addEvent({
title: item['title'],
rrule: {
dtstart: item['startDate'],
freq: item['frequency']
},
allDay: item['allDay'],
backgroundColor: backgroundRgb,
textColor: computeTextColor(backgroundRgb),
extendedProps: {
recurringEventID: item['recurringEventID'],
description: item['description']
},
});
Then a separate javascript bit modifies those individual occurrences as "done" and attaches the appropriate IndividualEventID to the occurrence.
var events = calendar.getEvents();
individualEventList.forEach(function(item){
events.forEach(function(event){
var indivdate = new Date(item['startDate']).toLocaleDateString('en-CA');
var eventdate = event.start.toLocaleDateString('en-CA');
//if the event is on the correct date and is a part of the rrule...
if(event.extendedProps.recurringEventID == item['recurringID'] && eventdate == indivdate)
{
console.log("match!");
event.setExtendedProp('singleID',item['individualEventID'])
//then modify its 'done' state
event.setExtendedProp('done',item['done']);
}
})
})
The "match!" console log fires only once, as I am expecting, when I modify a single event instance to be done and then load my database in. However all events in the rrule show up as done when I click on them/investigate their extendedProps.
It seems that modifying one event's extendedProps will modify all events' extendedProps if they're in the same rrule. How do I control this behavior? Can I break an event out of its rrule? Do I need to just do this computation in a different way?
I had to completely avoid dealing with extendedProps that are per single event. What I ended up doing:
When a user clicks on the event and I want to render it as done or not, I will check that events recurringEventID extendedProp, then check all IndividualEventList events to see if there's one that came from the same recurring event, and then check that the dates match. Then I'll check an HTML checkbox based off of the IndividualEventList event's done state.
In other words, I'm completely bypassing using extendedProps for anything other than "tell me which recurring event the event that was clicked belongs to".
Since I am assuming that all events will be all-day events this will work for me, but it's not a very general solution.

Possible to simulate select using setSelection?

I have a project where I am using the vis.js timeline module as a type of image carousel where I have a start and an end time, plot the events on the timeline, and cycle through them automatically and show the image attached to each event in another container.
I already have this working and use something similar to the following to accomplish this, except one part:
var container = document.getElementById('visualization');
var data = [1,2,3,4,5];
var timeline = new vis.Timeline(container, data);
timeline.on('select', function (properties) {
// do some cool stuff
}
var i = 0;
(function timelapseEvents(i) {
setTimeout(function(){
timeline.setSelection(data[i], {focus: true, animation:true});
if (i < data.length - 1) {
timelapseEvents(i+1);
}
}, 2000);
})(i)
The timeline.setSelection() part above works, the timeline event is selected and focused on. However, the "select" event is NOT triggered. This is verified as working as expected in the documentation (under Events > timeline.select) where it says: Not fired when the method timeline.setSelection() is executed.
So my question is, does anyone know how to use the timeline.setSelection() method and actually trigger the select event? Seems unintuitive to me to invoke the timeline.setSelection()method and not actually trigger the select event.
Spent a few hours on this and came up short. I ended up just taking the code I had in my timeline.on('select', function (properties) { block and turning it into a function and calling it after the timeline.setSelection() call.
Basically, I didn't fix the issue but worked around it. Will keep an eye on this in case anyone actually is able to figure out how to add the select() event to the setSelection() method.

Multiple fullacalendar widgets - synchronized view changes

I have a dashboard, where people can put multiple fullcalendar widgets (imagine a team dashboard with a fullcalendar widget for every person in a team). The main issue with this is performance. One client has 16 such widgets (all quite full of events) and they are not limited in number.
Only the first calendar has header controls (others have them hidden using CSS).
What I need is to propagate the changes to view (switch basicWeek to month etc.), firstDay (today in basicWeek, Monday everywhere else) and the date (prev, next, today) from first calendar to other calendars.
The issue is performance. As I didn't really find a way how to do it (that wouldn't look really hacky) except in viewRender, which is called for every single change. That means, if you switch from basicWeek to agendaWeek the first calendar propagates the firstDay to other calendars (which calls viewRender) and after that propagates the view change, which basically re-renders all calendars (except the first one) twice.
Is there a way to propagate those changes and manually call render on other calendars (from what I see in the source code, there might not be one), or a better way to do it?
I am also thinking about just destroying the calendars and re-initializing them with new options, but that might cause flashing (instead of quite a lot of lag caused by multiple re-renders). One of the options I thought of was using my own buttons (or re-using default buttons just unbinding original events from them), but even then I would still have to re-render the calendars multiple times in some occasions.
Switching view with 6 almost empty calendars takes about 3 seconds, which is unacceptable.
This is how my viewRender code looks like (this is inside own fullcalendar function, so every widget has its own scope with cached variables and settings)
viewRender: function(view) {
console.log('viewRender', calendarid, lastView, view.type);
// Propagate all changes from first calendar to other calendars on the page
if ($('#' + calendarid).parents('.widgetCalendar').is(':first-child')) {
// Change the first day (basicWeek is always today, other views are always Monday)
if (view.type != 'basicWeek' && currFirstDay != 1) {
currFirstDay = 1;
$('.calendarDiv').fullCalendar('option', 'firstDay', 1);
return;
} else if (view.type == 'basicWeek' && currFirstDay != firstday) {
currFirstDay = firstday;
$('.calendarDiv').fullCalendar('option', 'firstDay', firstday);
return;
}
// Propagate the view change to other calendars
if (lastView != view.type) {
lastView = view.type;
$('.calendarDiv:not(#' + calendarid + ')').fullCalendar('changeView', view.type); // , view.intervalStart.valueOf());
}
// Propagate the date change to other calendars
if (lastDate != view.intervalStart.valueOf()) {
lastDate = view.intervalStart.valueOf();
$('.calendarDiv:not(#' + calendarid + ')').fullCalendar('gotoDate', view.intervalStart.valueOf());
}
}
},
Ps.: At first I thought this issue is mainly ajax requests to get new events, but I changed that to a function which uses single call and caches the results. The main reason I thought that were some delays between ajax and that they weren't concurrent (session locking). But changing it to new function shows that the issue are indeed the re-renders which take white a long time per calendar (about 250 - 350ms).
If there is any info missing, ask in the comments and I will update the question.
Fullcalendar version: 3.4.0

FullCalendar fetch only events that are not shown currently

I have a web application that uses fullcalendar.io. I only have one event per day maximum. My problem is that everytime the events are fetched, it fetches all events, and this results into re-rendering of all the events in the month (at least). I don't want to refetch existing (in client already) events!
Why is this bad? Well, it's bad because the FullCalendar is programmed so that it will first hide/delete the clientside events from showing, then waits until the fetch is done, and then re-renders all the events. This results into a situation where for almost a second of time, the calendar month shows empty, before it renders the events. How I would like it to behave is: The calendar should fetch only the events that are not currently showed in that visible month. Then when it returns, it only re-renders those events that are new.
How I tried to achieve it is this:
events: {
data: function () {
return {
dynamic_value: JSON.stringify({myarray:$('#calendar').fullCalendar('clientEvents')})
};
},
url: '/calendar/events',
}
What I tried to do was to set a dynamic parameter, where I should put all the client side events. And then in the server backend, I would only send back events that are not included in that parameter. The problem is, that if I call the fullCalendar('clientEvents') in this place (inside the events object), it results in an empty array. So how could I give the events object a parameter to indicate that it should only fetch new events? Or am I approaching this the wrong way from the beginning?
Even if I would have found a way (I actually did) to pass old events to the dynamic_value (parameter), that wouldn't have solved the real problem.
And why is that? It is because I was using refetching for displaying the events. I looked under the hood of FullCalendar refetching function, and what it does is that it first deletes all the events from the calendar, and after that, it starts fetching new events. And during that ajax fetch, the calendar will stay blank. So it wouldn't helped me even if I had set the old events as a paremeter, and then applied serverside filtering.
But, luckily, I was able to solve the real problem. I continued fetching all events (I might change it in the future though), but I managed to avoid the problem. What I did was that I (again) looked under the hood. I realised that in FullCalendar there exists many un-documented functions that are usable through the api. One of them is a fetchEvents function (used by the refetchEvents function). The fetchEvents function only does the fetching, and it doesn't remove any old events. So what I did was that instead of using the reFetchEvents, I used:
$('#calendar').fullCalendar(
'fetchEvents',
$('#calendar').fullCalendar('getView').start,
$('#calendar').fullCalendar('getView').end
);
The fetchEvents function just needs the start and end as paramaters. I was able to get those by following how the fetchEvents function is used by refetchEvents function, and there they got the start and end from getView function. I was able to use the getView function as seen above.
But this is only half of the solution. Surely, I must somehow delete the old calendar events in a proper place. Well, for that, I was lucky to find also a function that could be used through the api (even not documented). I had to change the events part configuration in my fullCalendar. Instead of simple json feed URL, I changed it to ajax specification with success function, so that I could, in proper place, do the destroying of old events. So now the destroying part happens only after the ajax response, which will make the destroy to render process quick:
events: function(start, end, timezone, callback) {
$.ajax({
url: '/calendar/events',
dataType: 'json',
data: {
start: start.unix(),
end: end.unix()
},
success: function(doc) {
var events = [];
doc.forEach(function(eventObject) {
events.push({
title: eventObject.title,
start: eventObject.start,
user: eventObject.user,
allDay: eventObject.allDay,
overlap: eventObject.overlap,
created: eventObject.created
});
});
$('#calendar').fullCalendar('destroyEvents');
callback(events);
}
});
}
As you can see above, I use the fullCalendar destroyEvents before sending the new events for the callback to be rendered. And this solved the blank calendar during ajax call.

KendoUI MVVM Binding manually updating the source from displayed value

The situation:
I've extended a grid, and added an onkeydown event to listen for tab or arrow, whcih will allow the user to go to the next "editable" cell. I do this using ...
var grid = $("#" + that.gridId).data('kendoMyExtension');
grid.closeCell(currentCell);
grid.editCell(desiredCell);
Current behavior:
it works as expected however the when the cell closes, it doesn't persist the data (thru the correct binding) to the ViewModel.Field to which it is bound.
... However IFF you hit enter after making your changes, you will persist the changes.
What I've tried:
Manually making the update before I make the focus change (and fire off all those other kendo editing goodies) using
{grid Context}
that.saveRow();
that.dataSource.sync();
however these do not work. and usually end up throwing a undefined error somewhere in the bowels of kendo.
What I want:
Ideally kendo would supply at least one MVVM and kendo extension example (for a grid) that has all the functionality, events, etc. being bound to .. but.. since I will probably not get that asking here I will settle for:
Where does kendo "store" the changes?
What method is used to actually call the update {for a MVVM binding NOT a dataSource}.
Did I miss a binding keyword in the spirit of {one-way, two-way}?
Is kendo based on knockout.. can i use knockout techniques to get around the issues I'm having with kendo.
there are also a lot of other modifications to the display logic on this grid.. I am using a template to determine if the cell should be editable, a template to determine what should be rendered in nonedit & edit mode, and some IOC logic to wire it into the extension... (FYI)
o.k. after stepping thru kendo I found the issues... my datasource didn't have a reader (and it shouldn't afik) so sync and saveRow will not work reliably.
You MUST make sure the row is marked as 'k-edit-row' in your edit template :
{your edit template selector}.closest('tr').addClass("k-grid-edit-row");
if you want to get the saveRow to work, however in my case it wasn't reliable( it would only work if i stepped thru the code, otherwise fail every time...I have no clue as to why even tried the ever popular 'setTimeout(...)' ....)
eventually this is what I ended up doing:
myOnKeyDown:function(e){
var cell = $(e.target).closest("td")[0];
var row = $(e.target).closest("tr")[0];
if (cell != undefined) {
that.forceSync(cell, row, e.target.value);
}
},
forceSync: function (cell, row, value){
/// this is in the extension scope /////
/// Note that this uses functions in the dataSource Scope! ///
var cellFieldName = cell.kendoBindingTarget.target.options.fields.field;
var cellRecordUid = row.getAttribute('data-uid');
var that = $("#" + this.gridId).data('kendoMyExtension').dataSource, idx, length;
var data = that._flatData(that._data);
for (idx = 0, length = data.length; idx < length; idx++) {
if(data[idx].uid ===cellRecordUid){
data[idx][cellFieldName] = value;
}
}
},
... well after the considerable amount of time spent on this I felt that someone else should benefit...
Really hope it helps someone else.. -cheers.

Categories

Resources