Adding Dojo Date/Time Pickers In Traditional Web Development
I was presented with a challenge the other day. Add a date picker and time picker to a Notes form to be used in traditional web development (non-XPages). I could have chosen to use the pickers from JQuery or another framework, but those presented some problems:- They would have to download JQuery (which isn't allowed unless you go through layers of approvals)
- Even if there was download approval, they would have to remember to include JQuery in every application where this was being used
- To do it without downloading would require including public script libraries, which can sometimes raise red flags when the script source is from a different domain
The first thing to do is to include all the script libraries and style sheets. These go into the HTML Head Content for the form.
"<script type=\"text/javascript\">var dojoConfig = {locale: 'en-us', parseOnLoad: true};</script>" + @NewLine +
"<script type=\"text/javascript\" src=\"/xsp/.ibmxspres/dojoroot-1.9.7/dojo/dojo.js\"></script>" + @NewLine +
"<script type=\"text/javascript\" src=\"/xsp/.ibmxspres/dojoroot-1.9.7/ibm/xsp/widget/layout/layers/xspClientDojo.js\"></script>" + @NewLine +
"<script type=\"text/javascript\" src=\"/xsp/.ibmxspres/dojoroot-1.9.7/ibm/xsp/widget/layout/layers/xspClientDojoUI.js\"></script>" + @NewLine +
"<script type=\"text/javascript\">dojo.require(\"dojo.parser\")</script>" + @NewLine +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/xsp/.ibmxspres/dojoroot-1.9.7/dijit/themes/tundra/tundra.css\">" + @NewLine +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/xsp/.ibmxspres/dojoroot-1.9.7/ibm/domino/widget/layout/css/domino-default.css\">" + @NewLine +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/domjava/xsp/theme/webstandard/xsp.css\">" + @NewLine +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/domjava/xsp/theme/webstandard/xspLTR.css\">" + @NewLine +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/domjava/xsp/theme/webstandard/xspSF.css\">" + @NewLine +
"<script type=\"text/javascript\">dojo.require(\"dijit.form.Form\"); dojo.require(\"dijit.form.DateTextBox\"); dojo.require(\"dijit.form.TimeTextBox\");</script>" + @NewLine
Even though XPages is not being used at all here (it's a regular Notes form on the web), the JavaScript and CSS sources come from Dojo installed on the Domino sever because of XPages. Note that version 1.9.7 that is being used - that may need to change based on your installed version of Domino and/or the XPages extension library. Also note in the last line - both the DateTextBox and TimeTextBox are brought in. Obviously, if you're only using one of those, you don't need to bring in both. But everything else is the same whether you're using the Date Picker, the Time Picker, or both.
Next, in the HTML Body Attributes, add in a class that will make the dojo control(s) properly appear:
"class=\"tundra\""
Using the date field picker turned out to be a little easier than the time picker, so I'll cover the date picker first.
Adding a Dojo Date Picker
In the HTML Attributes on the date field, connect the field to the dojo control:
"id=\"StartDate\" data-dojo-type=\"dijit.form.DateTextBox\" data-dojo-props=\"constraints:{datePattern:'MM/dd/yyyy'}\""
The "ID" part should be changed to match the name of the field. Domino by default creates a "name" property on the web, but does not create an "ID" property, and that "ID" property will be needed. Note - you could create an "ID" property using the last tab of field properties -- the "<HTML>" tab on field properties. You can also change the format to suit your needs - the example above is a 2 digit month, slash, 2 digit day of month, slash, and 4 digit year.
That's all you really need to create the date picker. However, if you want to set a default value for the date picker, setting it in the Default Value section on the form doesn't work. It needs to be set after the page is loaded. You can do this in some pass through HTML on the form. For example, this pass through HTML will set the default value for the date picker to be the current date:
<script type="text/javascript">dojo.addOnLoad(function () { dijit.byId('StartDate').attr("value", new Date()); });</script>
This is an example of where adding the ID of "StartDate" to the field is used - getting the field by it's ID property.
If you want to set the default value to a computed date (like yesterday's date), then you can still do pass through HTML. But just add some Computed Text that has pass through HTML enabled. For the formula of the computed text, compute the date that should be used:
"<script type=\"text/javascript\">dojo.addOnLoad(function () { dijit.byId('DDate').attr(\"value\", new Date(" + @Text(@Year(@Yesterday)) + ", " + @Text(@Month(@Yesterday)-1) + ", " + @Text(@Day(@Yesterday)) + ")); });</script>"
You should be able to see the format for the date if you want to compute something else - it's a 4 digit year, comma, month (where 0 = January and 11 = December), comma, and the day of the month.
Adding a Dojo Time Picker
If you try to create the time picker in a similar fashion, you can, and it will appear to work, and will actually work as long as the time you choose has a minutes value between 0 and 30. The time picker creates a value of "T" followed by the hour (24-hour format) and minute. Like "T01:00" would be 1 AM and "T14:00" would be 2 PM. Those values are interpreted correctly by Domino - it's a time value with no date. But if you have a time value of "T03:45" for 3:45 AM, Domino will give you an "unable to interpret time or date" error.
The way to get around this and not have the error happen is to include both a date and a time. If the value is "01/01/2000T03:45" then it's interpreted properly and doesn't cause an error. But that also means there's a date associated with the time, and that isn't desired. Things have to be developed a bit differently to get the time picker to work properly.
First, in the HTML Attributes on the time field, set the field to be a hidden field and set the ID for the field:
"id=\"StartTime\" type=\"hidden\""
The "ID" part should be changed to match the name of the field. This time, the field that Domino generates is hidden. It will still be submitted to the server, but we will be creating a picker that is separate from the underlying field. For the date picker, the underlying field and the date picker control were one and the same.
To create the actual time picker, some pass through HTML needs to be added to the form right after the time field. Note that if this form is dual-use (used both in Notes and on the web) then you'll have to either hide the pass through HTML or put it in some computed text that uses @ClientType to not show anything to the Notes client. Here is the pass through HTML that will be needed to generate the time picker:
<input type="time" name="StartTimePicker" id="StartTimePicker" dojoType="dijit.form.TimeTextBox" data-dojo-props="maxHeight:250" value="T09:00" onchange="storeTimeValue('StartTime', 'StartTimePicker');" />
Some things to point out about the pass through HTML:
- The name and ID of "StartTimePicker" don't have to match anything on the form. In fact, you don't want them to match anything on the form. It kind of makes sense to take the name of the underlying field ("StartTime") and add the word "Picker" to the end, but really the name and ID can be whatever you want as long as you're not using those names anywhere else on your form.
- The property of "maxHeight:250" is something optional, but I just wanted to demonstrate it. If you omit it, the time picker can be pretty tall when you click on it. This sets the height for when you click on it. You can adjust to what you like.
- The value is the default value for the picker. I've already discussed the format above. Again, you could choose to compute this value through computed text if you want.
- The onchange event added to the picker is key - it will store the value chosen by the user in the picker back into the hidden field that ultimately is submitted to the Domino server.
function storeTimeValue(hiddenFieldId, pickerFieldId) {
var hiddenField = document.getElementById(hiddenFieldId);
var pickerField = document.getElementById(pickerFieldId);
var d1 = new Date();
var d2 = new Date((d1.getMonth()+1) + "/" + d1.getDate() + "/" + d1.getFullYear() + " " + pickerField.value);
var hours = d2.getHours();
var minutes = d2.getMinutes();
var ampm = (hours >= 12) ? "pm" : "am";
hours = hours % 12;
hours = (hours) ? hours : 12; // the hour "0" should be "12"
minutes = (minutes < 10) ? "0"+minutes : minutes; // Add leading zero
var strTime = hours + ":" + minutes + " " + ampm;
var strFullDate = (d2.getMonth()+1) + "/" + d2.getDate() + "/" + d2.getFullYear() + " " + strTime;
hiddenField.value = strFullDate;
}
It uses today's date as the date part and the value from the time picker as the time part and generates a value of month, slash, day, slash, year, space, hour, colon, minute, space, am/pm. That value is put into the hidden field that gets submitted to the server.
But the hidden field that gets submitted to the server will now have both a date and a time associated with it, and not just a time. This might not be a problem for some implementations, but it can be easily taken care of through an Input Translation formula on that field. If you use this input translation formula, it will remove any date part (and, incidentally, this formula will work just fine in the Notes client as well):
@TextToTime(@Text(@ThisValue; "T1S1"))