Monday, January 12, 2009

ColdFusion 8 - Multiple Date Selection Experiment with CFCALENDAR (HTML) and FLEX

For some time I have been wondering whether it was possible to select multiple dates with a cfcalendar. Technically, the answer is no.

After working with flash forms, I realized cfcalendar is based on the DateChooser component in Flex 1.5. Unfortunately, some of the niceties like selecting multiple dates or setting a minimum and maximum year displayed, were not introduced until Flex 2. I did eventually manage to simulate multiple date selections in flash forms, through a series of bizarre code gyrations, numerous expletives and light incantations. But having seen the calendar in Flex 2, I knew it was just a poor re-invention of the wheel.

But being the curious sort, I got to wondering if there was a way to use the Flex 2 calendar, from within ColdFusion 8. I began to realize it might be possible after reading a very interesting tip on Sam Farmer's blog about ColdFusion 8 and its Cool Ability to Compile Flex Applications. I could use it to create a swf containing a Flex 2 calendar. Then embed the calendar in my html form. The same way cfform does with cfcalendar. But I was not sure what to do from there.

It turns out there are several methods of communicating with javascript from Flash, such as the ExternalInterface introduced with Flash 8. As I understand it Flex 2 requires Flash 9+ anyway, so no problems there. Using my rudimentary Flex skills, I wrote a simple example that creates a calendar, enables multiple selections, and finally exposes the selectedRanges property to javascript using the addCallback method. (Loosely translated, selectedRanges is an array of structures. Each structure contains two keys: rangeStart and rangeEnd.) I then compiled the flex code, using the tip from Sam Farmer's blog.

<cfimport prefix="cfmxml" taglib="/WEB-INF/lib/cf-bootstrap-for-flex.jar">
<cfmxml:mxml>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
backgroundAlpha="0"
backgroundGradientAlphas="[0,0]"
backgroundColor="0xffffff"
creationComplete="onApplicationCreated();"
>
<mx:Script>
<![CDATA[
import flash.external.*;
import mx.controls.Alert;

protected function onApplicationCreated():void
{
if (!ExternalInterface.available)
{
Alert.show("JavaScript is required to use this calendar.", "Error");
return;
}

multiCalendar.allowMultipleSelection = true;

// this allows the actionscript function to be accessed from javascript
ExternalInterface.addCallback("getRanges", getSelectedRanges);
}

public function getSelectedRanges():Array
{
return multiCalendar.selectedRanges;
}
]]>
</mx:Script>

<mx:Style>
Application
{
background-color:"";
background-image:"";
padding: 0px;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
paddingBottom: 0;
paddingLeft: 0;
paddingRight: 0;
paddingTop: 0;
horizontalAlign: Left;
}
</mx:Style>

<mx:DateChooser id="multiCalendar" width="100%" height="100%"/>
</mx:Application>
</cfmxml:mxml>

That created a randomly named swf in my current directory. With the help of SWFObject, I embedded that swf in my html form. All that was left was to add a javascript function that retrieved and displayed the calendar dates in a javascript alert.


<html>
<head>
<script type="text/javascript" src="swfobject/swfobject.js"></script>
<script type="text/javascript">
// display the selected ranges from the cfcalendar object
function showRanges(id)
{
var ranges = document.getElementById(id).getRanges();
var arr = new Array();

arr.push("Selected ranges for: "+ id);
arr.push(" ");
for (var x in ranges)
{
arr.push("range["+ x +"]===========");
var obj = ranges[x];
for (var y in obj)
{
arr.push(y +"="+ obj[y]);
}
}
alert(arr.join("\n"));
}
</script>
</head>
<body>
<cfset calendarID = "myFlex2Calendar">

<cfoutput>
<script type="text/javascript">
swfobject.embedSWF("jspxxxxxxxxxxxxxx.swf","#calendarID#", "163","181", "9.0.0", "swfobject/expressInstall.swf");
</script>

<!--- container that will hold the final calendar --->
<div id="#calendarID#">
<p><a href="http://www.adobe.com/go/getflashplayer"><img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" border="0"/></a></p>
</div>
<form>
<input type="button" value="Show Dates" onClick="showRanges('#calendarID#');">
</form>
</cfoutput>
</body>
</html>


Voila! A calendar that allows multiple date selections.




Ultimately, I put the code in a cfc to make it completely dynamic. Then added in all of the basic cfcalendar settings, but also Flex 2 specific properties like the year navigation arrows and disabling multiple date ranges. Then passed those properties to flesh through flashVars. Here is an example of the end result. Ignore the ugly test form. If you look closesly, the calendar on the right has year navigation buttons, which I have always missed in cfcalendar.


Bear in mind my Flex skills are basic at best. So this was more of an experiment than anything else. But overall it was a great learning experience about one of the many things you can do with CF and Flex.

11 comments:

Todd Sharp January 15, 2009 at 6:16 AM  

Why not just build it in Flex rather then mess about with letting CF compile the SWF? There is no need for the MXML to be dynamic at all, right?

Cool idea though.

cfSearching January 15, 2009 at 7:19 AM  

@Todd,

No, the mxml would not change. I thought about it, but realized that would cause a recompile. So you are absolutely right. But I was just curious if you could tap into Flex 2 from CF8. Again, more of a experiment than anything else ;-)

- Leigh

Anonymous,  January 29, 2009 at 4:18 PM  

Hi Leigh,

Nice blog, but maybe time to learn a new Skill (Cold Fusion is soooo 1990s)

:)

-Bill G

cfSearching January 29, 2009 at 5:27 PM  

@Bill G,

To each his own (yawn) ;-)

Anonymous,  April 25, 2009 at 11:14 AM  

If cold fusion is so 90's... you would think that should have given you ample time to learn coldFusion is one word Bill ?

Paul Alkema May 26, 2009 at 2:02 PM  

Bill G,
Is it true that ColdFusion isn't the most popular language? Yes it’s true. However it's very immature to disregard a whole language just because of its age? Especially if you don’t know anything about it, which is obvious or you wouldn’t be disregarding it.
I personally write many different server-side languages and coldfusion is by far my favorite.
If you knew anything about it you would know how powerful it can be. For instance, in php in order to query a database you write…

<?php
$con = mysql_connect("localhost","peter","abc123");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db("my_db", $con);
$result = mysql_query("SELECT * FROM Persons");
while($row = mysql_fetch_array($result))
{
echo $row['FirstName'] . " " . $row['LastName'];
echo "<br />";
}
mysql_close($con);
?>
 

In coldFusion you write…
<cfquery name="result" datasource="testDsn">
SELECT * FROM Persons
</cfquery>

Please don’t insult a language that you obviously no nothing about.

Best Regards,
Paul Alkema
ColdFusion Evangelist

Mark Brodsky July 30, 2009 at 11:14 AM  

Hi Leigh,

Do you have a working demo available of your Flash Calendar where you can select multiple dates? (Multiple Date Selection Experiment - http://cfsearching.blogspot.com/2009/01/coldfusion-8-multiple-date-selection.html)

I would appreciate it very much!

Thanks,

-Mark

cfSearching July 30, 2009 at 12:51 PM  

@Mark,

I think I still have the full code on another machine. Let me see if I can find it and upload it.

-Leigh

Mark Brodsky July 30, 2009 at 2:11 PM  

That would be awesome, Leigh - thank you!

cfSearching July 30, 2009 at 5:49 PM  

@Mark,

I could not find the final code, but this version should be close. Though it is a bit rough. Let me know if you have any questions.


Download Example
.. or try the file download widget in the right hand menu.

-Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep