I need to fetch events every time a view is switched, and need to pass the view to the server.
However, the old view is still set at the time the events are requested from the server.
For example, the following alerts the view I am switching from, not the new view.
var calendar = new FullCalendar.Calendar(calendarEl, {
lazyFetching: false,
events:
{
url: '/events/get-events',
extraParams: function(){
alert(calendar.view.type);
}
}
}
Switch views in the following codepen
https://codepen.io/bestrong89/pen/RwPVmdY?editors=0010
Any ideas on a workaround?
Related
I have an event on one calendar, and I need to offer the user the ability to move that event to another month in the same calendar. It seems like the best way to do this is (1) remove the event from the current calendar, (2) create a draggable div outside the calendar, (3) allow the user to change the month/day/year, (4) allow the user to drag the div back onto the calendar, (5) hide the new div, (6) submit an ajax request to update the datasource, (7) delete the newly dropped event.
Attached are the code snippets used to do this.
My question: this seems very roundabout. Is there a better way?
// 1
reschedulingEvent = calendar.getEventById(....)
reschedulingEvent.remove()
// 2
rescheduledAppointment = new FullCalendar.Draggable(document.getElementById(
'rescheduled-appointment'), {
eventData: {
id: reschedulingEvent.id,
title: reschedulingEvent.title,
duration: "0:" + (reschedulingEvent.end - reschedulingEvent.start)/1000/60,
}
})
// 4
calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
editable: true,
droppable: true,
eventDrop: addEventToForm,
eventReceive: addEventToForm,
});
async function addEventToForm(info) {
if (!confirm("Would you like to reschedule this appointment")) return;
$(".rescheduled-appointment-div").hide() // 5
await axios.post(`/appointments/${info.event.id}`, {
start: info.event.start,
end: info.event.end,
}) // 6
calendar.refetchEvents()
calendar.getEventById(info.event.id).remove() // 7
Whenever the user selects the calendar I need to go back to the server and refresh the data for the dates they have selected - so on next, prev, etc. buttons. I tested that the event source works if defined on the calendar - but defined this way does not get the events. How do I hook up the views, next, prev buttons with the ajax call?
viewRender: function(view, element) {
var eventSource = {
url: '/JVCalendar/GetJVCalendarEvents',
type: 'POST',
data: {
start: "01/01/2015",
end: "01/31/2015",
calendarId: "1"
},
error: function() {
alert('there was an error while fetching events!');
}
}
$('#calendar').fullCalendar('addEventSource', eventSource);
$('#calendar').fullCalendar('refetchEvents');
}
I'll add this as an answer since it's only mentioned in the comments. Credits go to #smcd. Finally found this after a lot of hassle.
fullCalendar already sends the start and end date by default. When looking in the network logs I can see the parameters being added automatically.
api/v1/schedulings/fullcalendar?start=2016-02-28&end=2016-04-10&_=1458223901062
I am working on a page having lot of input-controls and related divs. There are use-cases on this page where I am suppossed to show/hide the divs depending on the order of user clicking on input-controls in various follow-up screens.
Now the divs are all there in first load itself and by showing/hiding, the screen changes for the user. Now to show/hide I can use css and add view* class to .main content div depending on business logic.
ex.:
.main div{
display: none;
}
.main.view1 div.a,.main.view1 div.b,.main.view1 div.f{
display:block;
}
.main.view2 div.c,.main.view2 div.f {
display:block;
}
.main.view3 div.c,.main.view3 div.f {
display:block;
}
....etc
But this way the no. of css classes are getting unmanageable.
Please suggest if there is a better method I can use wherein it becomes easy to manage the user-flows. I think there are regions in marionette which can help me manage this. Please suggest the best way and elaborate if the answer is marionette.regions
You can model the application as a state machine to model complicated workflows.
To define a state machine:
Define all the states that your application can be in.
Define the set of actions that are allowed in each state. Each action will transition the state of the application from one state to another.
Write the business logic for each action which includes both persisting changes to the server and also changing the state of the views accordingly.
This design is similar to creating a DFA, but you can add extra behaviour according to your needs.
If this sounds too abstract, here's an example of a simple state machine.
Let's say you're building a simple login application.
Design the States and Actions
INITIAL_STATE: The user visits the page for the first time and both fields are empty. Let's say you only want to make the username visible, but not the password in this state. (Similar to the new Gmail workflow)
USERNAME_ENTRY_STATE: When the user types in the username and hits return, in this state, you want to display the username and hide the password. You can have onUsernameEntered as an action in this state.
PASSWORD_ENTRY_STATE: Now, the username view will be hidden and the password view will be shown. When the user hits return, you have to check if the usernames and passwords match. Let's call this action onPasswordEntered
AUTHENTICATED_STATE: When the server validates the username/password combination, let's say you want to show the home page. Let's call this action onAuthenticated
I have omitted handling the Authentication Failed case for now.
Design the Views:
In this case, we have the UsernameView and the PasswordView
Design the Models:
A single Auth model suffices for our example.
Design the Routes:
Check out the best practices for handling routes with Marionette. The state machine should be initialized in the login route.
Sample Pseudo-Code:
I've only shown the code relevant to managing the state machine. Rendering and event handling can be handled as usual;
var UsernameView = Backbone.View.extend({
initialize: function(options) {
this.stateMachine = options.stateMachine;
},
onUserNameEntered: function() {
username = //get username from DOM;
this.stateMachine.handleAction('onUserNameEntered', username)
},
show: function() {
//write logic to show the view
},
hide: function() {
//write logic to hide the view
}
});
var PasswordView = Backbone.View.extend({
initialize: function(options) {
this.stateMachine = options.stateMachine;
},
onPasswordEntered: function() {
password = //get password from DOM;
this.stateMachine.handleAction('onPasswordEntered', password)
},
show: function() {
//write logic to show the view
},
hide: function() {
//write logic to hide the view
}
});
Each state will have an entry function which will initialize the views and and exit function which will cleanup the views. Each state will also have functions corresponding to the valid actions in that state. For example:
var BaseState = function(options) {
this.stateMachine = options.stateMachine;
this.authModel = options.authModel;
};
var InitialState = BaseState.extend({
entry: function() {
//show the username view
// hide the password view
},
exit: function() {
//hide the username view
},
onUsernameEntered: function(attrs) {
this.authModel.set('username', attrs.username');
this.stateMachine.setState('PASSWORD_ENTRY_STATE');
}
});
Similarly, you can write code for other states.
Finally, the State Machine:
var StateMachine = function() {
this.authModel = new AuthModel;
this.usernameView = new UserNameView({stateMachine: this});
//and all the views
this.initialState = new InitialState({authModel: this.authModel, usernameView: this.usernameView});
//and similarly, all the states
this.currentState = this.initialState;
};
StateMachine.prototype = {
setState: function(stateCode) {
this.currentState.exit(); //exit from currentState;
this.currentState = this.getStateFromStateCode(stateCode);
this.currentState.entry();
},
handleAction: function(action, attrs) {
//check if action is valid for current state
if(actionValid) {
//call appropriate event handler in currentState
}
}
};
StateMachine.prototype.constructor = StateMachine;
For a simple application this seems to be an overkill. For complicated business logic, it is worth the effort. This design pattern automatically prevents cases such as double-clicking on a button, since you would have already moved on to the next state and the new state does not recognise the previous state's action.
Once you have built the state machine, other members of your team can just plug in their states and views and also can see the big picture in a single place.
Libraries such as Redux do some of the heavy-lifting shown here. So you may want to consider React + Redux + Immutable.js as well.
FullCalendar is working great apart from 1 issue I'm having.
The monthview div which loads a calendar in monthview mode, seems to show duplicate holidays loaded in. This happens when I add an event, and then call my calendar bind function, which basically runs the code below.
Has anyone else had a similar issue? It looks like 'removeEvents' function is working ok against the data feed which comes from an internal database, but seems to leave the google dates. When the addEventSource is called, it's adding the same events again.
var googleUkHolidaysFeed = {
url: 'http://www.google.com/calendar/feeds/uk__en%40holiday.calendar.google.com/public/basic',
cache: true,
color: "green"
};
$.getJSON(url, {}, function (data) {
$('#dayview').fullCalendar('removeEvents');
$('#dayview').fullCalendar('addEventSource', data);
if ($("#monthview")[0]) {
$('#monthview').fullCalendar('removeEvents');
$('#monthview').fullCalendar('addEventSource', data);
$('#monthview').fullCalendar('addEventSource', googleUkHolidaysFeed);
}
});
I resolved this issue myself. The 'removeEvents' has to be called followed by 'removeEventSource' like so:
('data' is json array of events provided by the app, 'googleCalendarUkHolidayFeed' is the url feed from google).
var googleCalendarUkHolidayFeed = {
url: "http://www.google.com/calendar/feeds/bla..."
}
$('#dayview').fullCalendar('removeEvents');
$('#dayview').fullCalendar('addEventSource', data);
if ($("#monthview")[0]) {
// remove events and re-add event source to reflect search/non-search
$('#monthview').fullCalendar('removeEvents');
$('#monthview').fullCalendar('removeEventSource', googleCalendarUkHolidayFeed);
$('#monthview').fullCalendar('removeEventSource', data);
$('#monthview').fullCalendar('addEventSource', googleCalendarUkHolidayFeed);
$('#monthview').fullCalendar('addEventSource', data);
}
I am using the data table with my ASP.NET MVC 3 web application and so far it is going quite well. I connect to a SQL Server 2008 database, and I return data by using a stored procedure. I am using IE 8 and the latest version of Firefox. The version of YUI is 2.8.2r1. I have a couple of questions regarding the data table :)
Here is my data table's code:
<script type="text/javascript">
YAHOO.util.Event.onDOMReady(function () {
var grdNewsColumnDefs, grdNewsDataSource, grdNewsConfigs, grdNewsDataTable;
// News list data table
var formatActionLinks = function (oCell, oRecord, oColumn, oData) {
var newsId = oRecord.getData('NewsId');
oCell.innerHTML = 'Edit | ' +
'Details';
};
var formatActive = function (oCell, oRecord, oColumn, oData) {
if (oData) {
oCell.innerHTML = "Yes";
}
else {
oCell.innerHTML = "No";
}
};
grdNewsColumnDefs = [
{ key: 'Title', label: 'Title', className: 'align_left' },
{ key: 'Active', label: 'Active', className: 'align_left', formatter: formatActive },
{ key: 'Action', label: 'Actions', className: 'align_left', formatter: formatActionLinks }
];
grdNewsDataSource = YAHOO.util.DataSource('#Url.RouteUrl(Url.NewsJsonList())');
grdNewsDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
grdNewsDataSource.responseSchema = {
resultsList: 'DataResultSet',
fields: [
{ key: 'NewsId' },
{ key: 'Title' },
{ key: 'Active' },
{ key: 'Action' }
]
};
grdNewsConfigs = {
paginator: new YAHOO.widget.Paginator({
rowsPerPage: 20
})
};
grdNewsDataTable = new YAHOO.widget.DataTable('grdNews', grdNewsColumnDefs, grdNewsDataSource, grdNewsConfigs);
grdNewsDataTable.on('initEvent', function () {
YAHOO.util.Dom.setStyle(grdNewsDataTable.getTableEl(), 'width', '100%');
});
});
</script>
Not sure what I am doing wrong, but here is my action method that returns my data:
public ActionResult JsonList()
{
JsonEncapsulatorDto<News> data = new JsonEncapsulatorDto<News>
{
DataResultSet = newsService.FindAll()
};
return Json(data, JsonRequestBehavior.AllowGet);
}
I put a breakpoint on the return Json... line to see if this action method is hit. When the page loads the first time it goes to the break, I hit F5 then it runs and displays the view with the populated grid. When I refresh my browser by pressing F5 then my breakpoint is not hit again, I'm not sure why, it never goes in here again.
How is data loaded into the grid? If I have 100 records in the table and I have set my rowsPerPage to 20 then I will have 5 pages. Given my code above, is data loaded all at once, meaning is all 100 rows loaded at once? I would preferably like to have it loaded in "chunks" instead of having it all loaded at once. In another table I have much more records and this will not be a wise design approach to load everything at once. How would I implement something like this?
I am trying to style certain table headers and cells in the data table. I worked through this article explaining how to style a data table: http://www.satyam.com.ar/yui/widgetstyles.html. When I set the td to right align then the th for that column is also right aligned, why is this? You can see above how I set the className property. Here is my stylesheet code:
.yui-skin-sam .yui-dt td.align_left{text-align:left}
Given the above scenario, I want the column header to be left aligned and the corresponding column rows to right aligned? I probably won’t use it like this, but just want to know how to set a style to different elements?
I set the data table's width to be 100%, but when I page to the next page then it seems to loose this width of 100%. Why is this? What I need to do to have my data table to keep my width of 100%?
If I were to update data then it does not display as updated. Why is this and what do I need to do get the updated data to display in the data table?
You have configured your YUI grid to use an AJAX request to fetch the remote data:
grdNewsDataSource = YAHOO.util.DataSource('#Url.RouteUrl(Url.NewsJsonList())');
GET AJAX requests could be cached by the browser which explains why your controller action is hit only once (the first time you load the page). In order to avoid this caching you could either configure YUI to use a POST request or append a random number to the URL each time the page is loaded.
How is data loaded into the grid? If I have 100 records in the table and I have set my rowsPerPage to 20 then I will have 5 pages.
No matter what you set on the client side the following:
DataResultSet = newsService.FindAll()
is a clear indication that the server fetches all records from the database and sends all records back to the client and it is the client that retrieves only the necessary records to show which is inefficient.
Ideally the pagination should be done on the server. Here's an example from the documentation. The client sends the startIndex and results parameters to the server so that it could paginate the data set on the server and return only the necessary rows that will be shown on the screen.