Building an XPages Calendar Custom Control
Tags : XPages Calendar
Bookmark :
If you read my blog directly on my site and not through a RSS reader then you may have seen the nifty calendar that I wrote in BlogSphere. It's AJAX based and allows the reader to move backwards and forwards through the different months to see what other days have blog entries.
I wanted to recreate something very similar in XPages and started looking at a few options. The blogsphere version is a table which makes sense as it really is tabulated data but after thinking of a few ways to do it in XPages I decided to move to a pure CSS generated calendar. Each of the little squares in the calendar is a simple SPAN and the CSS is used to give them a border, float them to the left so they all line up after each other and then a surrounding DIV forces them to wrap around.
The header for the calendar is defined as simply as
<xp:span styleClass="calHeader">S</xp:span>
<xp:span styleClass="calHeader">M</xp:span>
<xp:span styleClass="calHeader">T</xp:span>
<xp:span styleClass="calHeader">W</xp:span>
<xp:span styleClass="calHeader">T</xp:span>
<xp:span styleClass="calHeader">F</xp:span>
<xp:span styleClass="calHeader">S</xp:span>
The next set of spans are the empty cells at the start of the calendar. Before I create the empty cells for the calendar I need to figure out how many there will be so I need a bit of server side javascript.
viewScope.dispCalYear = @Year(@Now());
viewScope.dispCalMonth = @Month(@Now());
viewScope.daysInMonth = new Date(viewScope.dispCalYear, viewScope.dispCalMonth, 0).getDate();
viewScope.firstDayInMonth = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1 ,1).getDay();
Here I get the current year and month. The number of days in the month is calculated by getting day zero of next month. JavaScript sees the request for day zero of a month as being a request for the last day of the previous month. It is a great shortcut because you don't need to worry about calculating leapyears, the javascript processor does all that for you. In javascript January is defined as 0 but in @formula language January is defined as 1. This explains why I don't need to add one to the month.
To get the first day in the month I pass in the 1st of the current month ( hence the -1 on the month variable ) and call the getDay function. This returns a 0 for Sunday, 1 for Monday and so on. If your doing a European style calendar where Monday is the first day on the left make sure you adjust the value returned here as required.
The number that we got back for the first day in the month is also the number of blank entries we need to pad the calendar with. To do this in XPages we can use a very simple repeat control. Repeat controls don't need to be linked to Domino data sources. In this case I'm setting the repeat's source as an integer and the repeat will repeat that many times. Inside the repeat control I just have a blank span.
Being able to do a repeat based on an integer also makes generating the actual calendar spans very easy also. We already have the number of days in the month thanks to the zero day trick explained above.
<xp:repeat id="repeat1" rows="31" value="#{javascript:viewScope.daysInMonth}" IndexVar="calIndex" var="calVar">
<xp:text escape="true" id="computedField1" value="#{javascript:calVar + 1}">
<xp:this.styleClass><!DATA[#{javascript:if (calIndex != 0){
if (calIndex + firstDayInMonth % 7 == 0){
return "firstday";
} else {
return "";
} } }>
</xp:this.styleClass>
<xp:this.converter>
<xp:convertNumber type="number" integerOnly="true" />
</xp:this.converter>
</xp:text>
</xp:repeat>
Make sure the repeats max is set to 31 ( the default is 30 and you'll wonder why certain months don't show that 31st day if you forget to change the default ). Inside my repeat I have a computed field set to display the repeats index plus one ( the index starts at zero ). I am also calculating the style class so that every eight span ( including the blank spans ) has a special css class assigned to it that ensures the row moves down to the next next row of the calendar.
We now have a very simple calendar in XPages. With some extra coding you can add some forward and backwards links above the calendar that will add or subtract one to the viewScope month/year variable and then do a partial update of the panel containing the calendar. Here's what my final calendar looks like with the extra coding completed.
As you can see I have also redone my calendar CSS so that it blends in well with the oneUI scheme.
Bookmark :
If you read my blog directly on my site and not through a RSS reader then you may have seen the nifty calendar that I wrote in BlogSphere. It's AJAX based and allows the reader to move backwards and forwards through the different months to see what other days have blog entries.
I wanted to recreate something very similar in XPages and started looking at a few options. The blogsphere version is a table which makes sense as it really is tabulated data but after thinking of a few ways to do it in XPages I decided to move to a pure CSS generated calendar. Each of the little squares in the calendar is a simple SPAN and the CSS is used to give them a border, float them to the left so they all line up after each other and then a surrounding DIV forces them to wrap around.
The header for the calendar is defined as simply as
<xp:span styleClass="calHeader">S</xp:span>
<xp:span styleClass="calHeader">M</xp:span>
<xp:span styleClass="calHeader">T</xp:span>
<xp:span styleClass="calHeader">W</xp:span>
<xp:span styleClass="calHeader">T</xp:span>
<xp:span styleClass="calHeader">F</xp:span>
<xp:span styleClass="calHeader">S</xp:span>
The next set of spans are the empty cells at the start of the calendar. Before I create the empty cells for the calendar I need to figure out how many there will be so I need a bit of server side javascript.
viewScope.dispCalYear = @Year(@Now());
viewScope.dispCalMonth = @Month(@Now());
viewScope.daysInMonth = new Date(viewScope.dispCalYear, viewScope.dispCalMonth, 0).getDate();
viewScope.firstDayInMonth = new Date(viewScope.dispCalYear,viewScope.dispCalMonth -1 ,1).getDay();
Here I get the current year and month. The number of days in the month is calculated by getting day zero of next month. JavaScript sees the request for day zero of a month as being a request for the last day of the previous month. It is a great shortcut because you don't need to worry about calculating leapyears, the javascript processor does all that for you. In javascript January is defined as 0 but in @formula language January is defined as 1. This explains why I don't need to add one to the month.
To get the first day in the month I pass in the 1st of the current month ( hence the -1 on the month variable ) and call the getDay function. This returns a 0 for Sunday, 1 for Monday and so on. If your doing a European style calendar where Monday is the first day on the left make sure you adjust the value returned here as required.
The number that we got back for the first day in the month is also the number of blank entries we need to pad the calendar with. To do this in XPages we can use a very simple repeat control. Repeat controls don't need to be linked to Domino data sources. In this case I'm setting the repeat's source as an integer and the repeat will repeat that many times. Inside the repeat control I just have a blank span.
<xp:repeat id="calBlanks" rows="7" value="#{javascript:viewScope.firstDayInMonth}" var="calblankVar">
<xp:span styleClass="calBlank" />
</xp:repeat>
Being able to do a repeat based on an integer also makes generating the actual calendar spans very easy also. We already have the number of days in the month thanks to the zero day trick explained above.
<xp:repeat id="repeat1" rows="31" value="#{javascript:viewScope.daysInMonth}" IndexVar="calIndex" var="calVar">
<xp:text escape="true" id="computedField1" value="#{javascript:calVar + 1}">
<xp:this.styleClass><!DATA[#{javascript:if (calIndex != 0){
if (calIndex + firstDayInMonth % 7 == 0){
return "firstday";
} else {
return "";
} } }>
</xp:this.styleClass>
<xp:this.converter>
<xp:convertNumber type="number" integerOnly="true" />
</xp:this.converter>
</xp:text>
</xp:repeat>
Make sure the repeats max is set to 31 ( the default is 30 and you'll wonder why certain months don't show that 31st day if you forget to change the default ). Inside my repeat I have a computed field set to display the repeats index plus one ( the index starts at zero ). I am also calculating the style class so that every eight span ( including the blank spans ) has a special css class assigned to it that ensures the row moves down to the next next row of the calendar.
We now have a very simple calendar in XPages. With some extra coding you can add some forward and backwards links above the calendar that will add or subtract one to the viewScope month/year variable and then do a partial update of the panel containing the calendar. Here's what my final calendar looks like with the extra coding completed.
As you can see I have also redone my calendar CSS so that it blends in well with the oneUI scheme.
Comments
Posted by Colin Williams At 04:27:23 PM On 05/26/2009 | - Website - |
Posted by Bruce Elgort At 10:17:11 AM On 05/27/2009 | - Website - |
Posted by Mike McGarel At 11:37:21 AM On 05/27/2009 | - Website - |
Posted by solomon At 12:24:48 PM On 06/08/2009 | - Website - |