Showing posts with label range. Show all posts
Showing posts with label range. Show all posts

Saturday, 3 March 2012

Integrate Custom Date Picker with Visualforce

If you've used Visualforce input fields backed by a Date or DateTime sobject field, one thing you'll have noticed is the date range isn't that helpful.  For example, the screenshot below is from my developer edition and the starting year is 2011.

 
 
 
This is fine for opportunities, cases, campaigns etc., that have dates in the future, but less than useful when recording a date in the past - for example year of birth.  There's a couple of options here.  The first is to use some javascript on the page to tweak the setup of the date picker.  Not a solution I'd recommend, as a change to the underlying implementation would break the page.
 
The other option is to use a custom date picker.  I favour the Design2Develop implementation, as a lot of the others have style class names that overlap with the Salesforce implementation, which could cause problems if I wanted to combine both on the same page.
 
The first thing to do is get the zip file from the download page and then upload it as a static resource to your Salesforce organization.  I chose the name JSCalendar - if you choose a different one, change the $Resource entries in the sample code.
 
Next, create the Visualforce page and add following lines to include the core Javascript and styling:
 
(UPDATE 09/01/2014 - the latest zip needs the calendar folder specified as the location for the JavaScript and CSS - if you are having problems make sure these your imports match these lines)
<apex:includeScript value="{!URLFOR($Resource.JSCalendar,’calendar/calendar.js')}"/>
<apex:stylesheet value="{!URLFOR($Resource.JSCalendar,’calendar/calendar_blue.css')}" />

I've gone for the blue style, but there are several others in the zip file.   Next, define the date format that you'll be using - this is achieved by implementing the fnSetDateFormat function:

function fnSetDateFormat(oDateFormat)
{
 oDateFormat['FullYear'];  //Example = 2007
 oDateFormat['Year'];   //Example = 07
 oDateFormat['FullMonthName']; //Example = January
 oDateFormat['MonthName'];  //Example = Jan
 oDateFormat['Month'];   //Example = 01
 oDateFormat['Date'];   //Example = 01
 oDateFormat['FullDay'];   //Example = Sunday
 oDateFormat['Day'];    //Example = Sun
 oDateFormat['Hours'];   //Example = 01
 oDateFormat['Minutes'];   //Example = 01
 oDateFormat['Seconds'];   //Example = 01
 
 var sDateString;
 
 // Use dd/mm/yyyy format
 sDateString = oDateFormat['Date'] +"/"+ oDateFormat['Month'] +"/"+ oDateFormat['FullYear'];
 return sDateString;
}

Next, define the function that will manage the integration between the date picker and the input element. This takes two parameters - the object that fired the calendar popup (e.g. an image or button) and the id of the input element that contains the date.  This function will be called when the picker is displayed, and is responsible for extracting the current value from the input element and passing that to the library function.

function initialiseCalendar(obj, eleId)
{
 var element=document.getElementById(eleId);
 var params='close=true';
 if (null!=element)
 {
  if (element.value.length>0)
  {
   // date is formatted dd/mm/yyyy - pull out the month and year
   var month=element.value.substr(3,2);
   var year=element.value.substr(6,4);
   params+=',month='+month;
   params+=',year='+year;
  }
 }
 fnInitCalendar(obj, eleId, params);
}

Finally, add an input component.  Its possible to change the standard behaviour of the apex:inputField using javascript to tie to to the custom picker as opposed to the standard, but again this is a fragile solution and would preclude using standard and custom on the same, so I prefer use an apex:inputText instead.  As can be seen, the object that is passed to the initialiseCalendar function is the input field that is also identified by the id parameters - I could change the function to take a single parameter, but if I then wanted to have an image that is clicked on to open the calendar I'd be out of luck. I've made this the onmouseover event handler so that it opens in similar way to the standard picker.

<apex:inputText id="startdate" size="10" value="{!Campaign.StartDate}" onmouseover="initialiseCalendar(this, '{!$Component.startdate}')"/>

Here's a screen shot of the new picker:
There's a fair bit more you can do to customize this picker - check out the docs on the download page.

For the sake of completeness here is the full markup of the Visualforce page:

<apex:page standardController="Campaign">
<apex:includeScript value="{!URLFOR($Resource.JSCalendar,’calendar/calendar.js')}"/>
<apex:stylesheet value="{!URLFOR($Resource.JSCalendar,’calendar/calendar_blue.css')}" />
 <apex:pageMessages />
 <apex:form id="frm">
   <apex:pageblock id="pb">
     <apex:pageBlockButtons >
        <apex:commandButton value="Save" action="{!save}" />
        <apex:commandButton value="Cancel" action="{!Cancel}" />
     </apex:pageBlockButtons>
     <apex:pageBlockSection id="pbs">
     <apex:inputField value="{!Campaign.Name}" />
  <apex:inputText id="startdate" size="10" value="{!Campaign.StartDate}" onmouseover="initialiseCalendar(this, '{!$Component.startdate}')"/>
  <apex:inputText id="enddate" size="10" value="{!Campaign.EndDate}" onmouseover="initialiseCalendar(this, '{!$Component.enddate}')"/>
 </apex:pageBlockSection>
   </apex:pageblock>
 </apex:form>
 <script>
function fnSetDateFormat(oDateFormat)
{
 oDateFormat['FullYear'];  //Example = 2007
 oDateFormat['Year'];   //Example = 07
 oDateFormat['FullMonthName']; //Example = January
 oDateFormat['MonthName'];  //Example = Jan
 oDateFormat['Month'];   //Example = 01
 oDateFormat['Date'];   //Example = 01
 oDateFormat['FullDay'];   //Example = Sunday
 oDateFormat['Day'];    //Example = Sun
 oDateFormat['Hours'];   //Example = 01
 oDateFormat['Minutes'];   //Example = 01
 oDateFormat['Seconds'];   //Example = 01
 
 var sDateString;
 
 // Use dd/mm/yyyy format
 sDateString = oDateFormat['Date'] +"/"+ oDateFormat['Month'] +"/"+ oDateFormat['FullYear'];
 return sDateString;
}
  
    
function initialiseCalendar(obj, eleId)
{
 var element=document.getElementById(eleId);
 var params='close=true';
 if (null!=element)
 {
  if (element.value.length>0)
  {
   // date is formatted dd/mm/yyyy - pull out the month and year
   var month=element.value.substr(3,2);
   var year=element.value.substr(6,4);
   params+=',month='+month;
   params+=',year='+year;
  }
 }
 fnInitCalendar(obj, eleId, params);
}
 </script>
</apex:page>