Aug
14
Written by:
Michael Washington
8/14/2013 7:45 PM
The ComponentOne Studio for LightSwitch HTML suite of controls contains an Events Calendar control that allows you to create some really incredible applications. In this example we will use it to build a Visual Studio LightSwitch application that will allow employees to enter their vacations and time off so that their organization can easily track employee availability.
You can find full documentation for all the controls in the suite at this link:
http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/
In this tutorial we will not go over every step, so if you are new to Visual Studio LightSwitch HTML Client, it is recommended that you start here: Online Ordering System (An End-To-End LightSwitch Example).
The first page that opens is designed for mobile devices. When you open it, the default day is today. It allows you to easily see the employees who are off on that day. You can change the start and stop times to see employees who are off between the selected days.
Employees (people) can be administered in the application.
The demographic data for the employees is stored and administered.
Clicking the Vacations tab, when you have an employee selected, allows you to see and edit their vacations.
When viewing a vacation you can call or email the employee with a simple click on the links.
The ComponentOne Calendar control is designed for desktop use. It shows the entries in a daily view.
… a weekly view…
…a monthly view…
..and in a list.
You can change the start and stop times of entries by dragging the top and bottom of each block, or dragging and dropping a block.
Double clicking on a block allows you to edit it. Double clicking on a blank space on the calendar allows you to create a new entry.
Creating the Application
First, you will need to download and install the free trial:
http://www.componentone.com/SuperProducts/LightSwitchHTML/
In Visual Studio 2012 with the latest service updates (or higher), we create a New Project.
We create a Visual Studio LightSwitch HTML Application.
Next, we follow the directions on the ComponentOne site:
Creating a ComponentOne Studio for LightSwitch HTML Project
Make sure you follow all directions including: Installing the NuGet Client Package.
Next we create People and Vacation tables.
We want to prevent a user from creating a Vacation with an invalid date.
On the Vacation table, we select the _Validate method and use the following code:
partial void Vacations_Validate(Vacation entity, EntitySetValidationResultsBuilder results)
{
if (entity.StartDate > entity.EndDate)
{
results.AddEntityError(
String.Format("End Date must be greater than {0}", entity.StartDate)
);
}
}
To set the default values whenever a Vacation is created, open the Vacation table and select the HTMLClient tab. Then select Write Code then the created method.
Use the following code for the method:
myapp.Vacation.created = function (entity) {
var dtStartDate = new Date();
dtStartDate.setHours(8, 0, 0);
var dtEndDate = new Date();
dtEndDate.setHours(17, 0, 0);
entity.StartDate = dtStartDate;
entity.EndDate = dtEndDate;
};
Now we want to create the query that will be used on the main screen.
We right-click on the Vacations table and select Add Query.
We name the query GetVacationsBetweenDates , create StartDate and EndDate parameters, and select PreprocessQuery.
We use the following code for the query (note, you will need to add using System.Linq.Expressions to the top of the class):
partial void GetVacationsBetweenDates_PreprocessQuery(
DateTime?
StartDate,
DateTime?
EndDate,
ref IQueryable<Vacation> query)
{
if (StartDate.HasValue && EndDate.HasValue)
{
var dtStartDate = StartDate.Value.Date;
var dtEndDate = EndDate.Value.Date;
query = query
.Where(x =>
EntityFunctions.CreateDateTime(
x.StartDate.Year,
x.StartDate.Month,
x.StartDate.Day,
0, 0, 0)
<= dtEndDate
&& EntityFunctions.CreateDateTime(
x.EndDate.Year,
x.EndDate.Month,
x.EndDate.Day,
0, 0, 0)
>= dtStartDate
);
}
else
{
// Return nothing
query.Where(x => x.Id == 0);
}
}
Client Side Code
We want to create the main screen that will show the employees who are on vacation.
The first step is to make a new screen that we will call Main. When the screen opens, perform the following steps:
- Click the Add Data Item button to open the Add Data Item dialog.
- Click Query.
- Select the GetVacationsBetweenDates query we created earlier.
- Click OK.
- The Query will appear in the View Model on the left side of the screen designer. StartDate and EndDate properties will be automatically created and hooked to the parameters the query requires.
- Drag and drop the query to the screen layout.
- The StartDate and EndDate properties will be automatically inserted into the screen layout.
Switch the label controls for the Start Date and End Date to Date/Time Pickers.
To set the defaults, to pull up the current day, we first select the created method and use the following code:
myapp.Main.created = function (screen) {
// Set defaults
var dtToday = new Date();
dtToday.setHours(23, 59, 0);
var dtTomorrow = new Date();
dtTomorrow.setDate(dtTomorrow.getDate() + 1);
dtTomorrow.setHours(0, 0, 0);
screen.StartDate = dtToday;
screen.EndDate = dtTomorrow;
screen.details.displayName = "Vacation Tracker";
};
Alter the screen layout so that it resembles the image above.
Add / Edit Vacations
Create a AddEditVaction screen for the Vacation table.
Change the screen layout so that it resembles the image above.
To display the Email and Phone properties, use the Other Screen Data option.
Select the associated People table and then enter a period (“.”) and then select the property.
We now want to open the Add / Edit Vacation screen from the Main screen.
Open the Main screen, and add a button to the Command Bar.
In the Add Button dialog, select Write my own method and call the method AddVacation_Tap.
Right-click on the method in the View Model and select Edit Execute Code.
Use the following code for the method:
myapp.Main.AddVacation_Tap_execute = function (screen) {
myapp.showAddEditVacation(null, {
beforeShown: function (addEditVacationScreen) {
// Create new Vacation here so that
// discard will work.
var newVacation = new myapp.Vacation();
addEditVacationScreen.Vacation = newVacation;
},
afterClosed: function (addEditVacationScreen, navigationAction) {
// If the user commits the change,
// show the new Vacation in View Screen.
if (navigationAction === msls.NavigateBackAction.commit) {
// Refresh Query
screen.GetVacationsBetweenDates.load();
}
}
});
};
To edit an existing Vacation, click on the List control and select the Item Tap event.
In the Edit ItemTap Action dialog, select Write my own method and click OK.
Right-click on the method in the View Model and select Edit Execute Code.
Use the following code for the method:
myapp.Main.GetVacationsBetweenDates_ItemTap_execute = function (screen) {
myapp.showAddEditVacation(null, {
beforeShown: function (addEditVacationScreen) {
// edit the selected vacation
addEditVacationScreen.Vacation = screen.GetVacationsBetweenDates.selectedItem;
},
afterClosed: function (addEditVacationScreen, navigationAction) {
// If the user commits the change,
// show the new Vacation in View Screen.
if (navigationAction === msls.NavigateBackAction.commit) {
// Referesh Query
screen.GetVacationsBetweenDates.load();
}
}
});
};
We add BrowsePeople and AddEditperson screens and wire them up to the application.
Note: You can download the complete application on the Download page at: http://lightswitchhelpwebsite.com/Downloads.aspx.
If you are new to Visual Studio LightSwitch HTML Client, it is recommended that you start here: Online Ordering System (An End-To-End LightSwitch Example).
When we run the application we have the ability to add and edit Vacations.
Using The ComponentOne Event Scheduler Control
To add the ComponentOne Events Calendar, we create a screen and use the Wijmo Events Calendar Screen template connected to the Vacations table.
When we run the application and navigate to the screen we see that the calendar displays with the vacation entries.
Yes, it is that easy!
Customizing The ComponentOne Event Scheduler Control
We now desire to implement the following additional functionality:
- Show the name of the person (from the associated Person table)
- Allow items to be dragged and dropped to be moved
- Allows items to be resized to change their dates
- Allow existing items to be edited by clicking on them
- Allow new items to be created by double clicking on a blank space on the calendar
- Only load one month’s worth of data at a time
The good news is that the control is fully customizable and has excellent documentation.
The main documentation for the entire suite of controls is here:
http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/
The main documentation of the Events Calendar control is here:
http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/#!Documents/wijmoeventscalendars1.htm
Additional documentation for the Events Calendar is here:
http://helpcentral.componentone.com/nethelp/c1htmlclientlightswitch/#!Documents/eventscalendar1.htm
Detailed documentation for the wijevcal (used by the Events Calendar) is here:
http://wijmo.com/docs/wijmo/webframe.html#Eventscalendar.html
Complete detailed API documentation for the wijevcal is here:
http://wijmo.com/docs/wijmo/webframe.html#Wijmo~jQuery.fn.-~wijevcal.html
The first step is to change the configuration for the control in the screen designer so that the Id field is the third property passed to the control.
Passing the Id field will allow the control to properly identify the calendar item. We will add code that will perform an asynchronous lookup to pull in the other data for the calendar item such as the associated Person.
Next, we click on the root node in the screen designer, and in the Properties we set the screen to Edit Screen Type.
Make A Custom Query
To support only retrieving the data for the current selected month, we click the Edit Query button next to the Vacations collection in the screen’s View Model on the left-hand side of he screen.
Edit the query to take a StartDate and EndDate parameter.
Click the back button to return to the screen designer.
Use the Add Data Item button to add two Data Items of the type of Date to the screen and connect them to the parameters for the query.
You do not need to add the Data Items to the screen layout.
Custom JavaScript Code
To write our custom code, we click the Write Code button.
We add the following code to hold our global variables we will need:
var _calendarControl;
var _screen;
var _currentMonth = new Date();
var _lastSelectedDate = new Date();
Next, we add the following created method that will set the global variables and call the method to load the data for the month:
myapp.VacationsCalendar.created = function (screen) {
// Set _screen global variable
_screen = screen;
// Set defaults
_currentMonth = new Date(1900, 1, 1, 0, 0, 0, 0);
// Get data for the current date
GetDataForTheMonth(new Date());
};
The following method will load the data for the month:
function GetDataForTheMonth(startingDate) {
// Convert the passed date to the month
var dtDate = new Date(startingDate);
var paramMonth = new Date(dtDate.getFullYear(), dtDate.getMonth(), 1, 0, 0, 0);
_currentMonth = new Date(_currentMonth.getFullYear(), _currentMonth.getMonth(), 1, 0, 0, 0);
// Only update data if current month has changed
if (paramMonth < _currentMonth || paramMonth > _currentMonth) {
// Set _currentMonth
_currentMonth = new Date(paramMonth);
// Set the date defaults
var dtStartDate = new Date(_currentMonth);
dtStartDate.setMonth(dtStartDate.getMonth());
dtStartDate.setHours(0, 0, 0);
var dtEndDate = new Date(_currentMonth);
dtEndDate.setMonth(dtEndDate.getMonth() + 1);
dtEndDate.setHours(0, 0, 0);
_screen.paramStartDate = dtStartDate;
_screen.paramEndDate = dtEndDate;
// refresh the collection
_screen.Vacations.load();
}
}
A _render method already exists. It was created automatically by the ComponentOne plug-in. However, we want to replace it with our custom code:
myapp.VacationsCalendar.WijmoGrid_render = function (element, contentItem) {
$.wijmo.wijcalendar.prototype.options.wijCSS.stateDefault = "ui-btn-up-a";
$.wijmo.wijcalendar.prototype.options.wijCSS.content = "ui-body ui-body-a";
_calendarControl = $("<div/>");
_calendarControl.appendTo($(element));
_calendarControl.attr("style", "width: 800px; height: 640px");
setTimeout(function () {
_calendarControl.wijevcal({
viewType: "day",
viewTypeChanged: function (e) {
$(".wijmo-wijev-headerbar .wijmo-wijev-tools").controlgroup("refresh");
},
beforeUpdateEvent: function (e, data) {
// Get the Vacation Item to be updated
var filter = "(Id eq " + msls._toODataString(data.data.VacationId, ":Int32") + ")";
myapp.activeDataWorkspace.ApplicationData.Vacations.filter(filter)
.execute().then(function (result) {
// Get the results of the query
var existingVacation = result.results[0];
// Update the vacation
existingVacation.StartDate = data.data.start;
existingVacation.EndDate = data.data.end;
});
},
selectedDatesChanged: function (e, data) {
// Get the minimum date from the array of dates
var dates = [];
$.each(data.selectedDates, function (key, value) {
dates.push(new Date(value))
});
var sorted = dates.sort(function (a, b) {
return a.getTime() - b.getTime();
});
_lastSelectedDate = sorted[0];
// Possibly refresh the data for the select month
GetDataForTheMonth(_lastSelectedDate);
}
});
$(".wijmo-wijev-headerbar .wijmo-wijev-tools").controlgroup("refresh");
_calendarControl.wijevcal("invalidate");
refreshCalendar(contentItem);
}, 100);
contentItem.value.oncollectionchange = function () {
refreshCalendar(contentItem);
};
};
The code above calls a refreshCalendar method, we will add that now:
function refreshCalendar(calendarContent) {
var calendar = c1ls.getCalendarContent(calendarContent);
_calendarControl.wijevcal({
timeInterval: 60,
timeIntervalHeight: 20,
beforeEditEventDialogShow: calendar.Select
});
// We want the name of the event to be the name of the
// person
$.each(calendar, function (key, value) {
if (key == 'Events') {
// Loop thru each event
$.each(value, function (key, vacationEvent) {
// To assign the label for the event to the
// name of the person in the associated
// People table we need to make a few queries
// The vacationId is the subject
// Assign it to vacationId
var vacationId = vacationEvent.subject;
// So that we can update the Vacation event later
// Create a VacationId property and set it
vacationEvent.VacationId = vacationId;
// For now set the subject to "loading..."
// It will be updated by the async method (below)
vacationEvent.subject = "loading...";
// Create a filter
var filter = "(Id eq " + msls._toODataString(vacationId, ":Int32") + ")";
// Get the Vacation
myapp.activeDataWorkspace.ApplicationData.Vacations.filter(filter)
.execute().then(function (result) {
// Get the Person
result.results[0].getPerson().then(function (result) {
// Set the Person's name
vacationEvent.subject = result.Name;
// Update the Calendar with the updated data
_calendarControl.wijevcal("option", "eventsData", calendar.Events);
});
});
});
}
});
if (calendar.Events[0] == null) {
// Update the Calendar with the updated data
_calendarControl.wijevcal("option", "eventsData", calendar.Events);
}
}
Editing Calendar Items
To allow calendar items to be edited in a popup, we return to the screen designer and click on the Colums layout for the control and select the tap event.
We create a method called EditSelectedVacation.
We select the method in the View Model and select Edit Execute Code.
We use the following code for the method:
myapp.VacationsCalendar.EditSelectedVacation_execute = function (screen) {
myapp.showAddEditVacation(null, {
beforeShown: function (addEditVacationScreen) {
if (screen.Vacations.selectedItem != null) {
// Edit selected Vacation
addEditVacationScreen.Vacation = screen.Vacations.selectedItem;
} else {
// Create new Vacation
var newVacation = new myapp.Vacation();
// Create start and end times
var dtStartDate = new Date(_lastSelectedDate);
dtStartDate.setHours(8, 0, 0);
var dtEndDate = new Date(_lastSelectedDate);
dtEndDate.setHours(17, 0, 0);
newVacation.StartDate = dtStartDate;
newVacation.EndDate = dtEndDate;
// set new Vacation
addEditVacationScreen.Vacation = newVacation;
}
},
afterClosed: function (addEditVacationScreen, navigationAction) {
// If the user commits the change,
// show the new Vacation in View Screen.
if (navigationAction === msls.NavigateBackAction.commit) {
// Save changes
SaveCalendarChanges();
}
}
});
};
That method calls a SaveCalendarChanges method. We add that using the following code:
function SaveCalendarChanges() {
// Save changes
myapp.applyChanges().then(function () {
// Referesh Query
_screen.Vacations.load();
var calendar = c1ls.getCalendarContent(screen.Vacations);
// Update the Calendar with the updated data
_calendarControl.wijevcal("option", "eventsData", calendar.Events);
}, function fail(e) {
// If error occurs, show the error.
msls.showMessageBox(e.message, { title: e.title }).then(function () {
// Discard Changes
_screen.details.dataWorkspace.ApplicationData
.details.discardChanges();
});
});
}
Download Code
The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx
(you must have Visual Studio 2012 (or higher) installed and the ComponentOne Studio for LightSwitch HTML plug-ins to run the code)