Viewing By Category : flash forms / Main
August 30, 2005
make cfselect your sock puppet
i had the need to keep a cfselect synched up w/other cfform objects like grids and trees that were being changed by user inputs (managing IMAP mail folders for instance). digging around the flex docs again, it seems cfselect is part of the mx.controls.listclasses package and you can indeed manipulate it quite a bit using that package's DataSelector/DataProvider class methods. for me the interesting methods were:
  • getItemAt()
  • removeItemAt()
  • addItemAt()
  • replaceItemAt()

and the selectedIndex property. you can do a lot with a bit of action script:

<cfform name="testForm" format="Flash">
<cfselect name="test" size="1" visible="Yes" enabled="Yes" width="200">
<cfoutput>
<cfloop index="i" from="1" to="10">
<option value="#i#">test #i#</option>
</cfloop>
</cfoutput>
</cfselect>
<cfinput type="Button" name="a" value="length" visible="Yes" enabled="Yes"
   onclick="alert(test.length);">

<cfinput type="Button" name="b" value="get at 5" visible="Yes" enabled="Yes"
   onclick="alert('value@5: '+test.getItemAt(4).data);">

<cfinput type="Button" name="c" value="remove at 5" visible="Yes" enabled="Yes"
   onclick="test.removeItemAt(4); alert('removed');">

<cfinput type="Button" name="d" value="add at 5" visible="Yes" enabled="Yes"
   onclick='test.addItemAt(4,"newer test",5);alert("added");'>

<cfinput type="Button" name="e" value="change at 5" visible="Yes" enabled="Yes"
   onclick='test.replaceItemAt(4,"replaced item",5); alert("replaced");'>

<cfinput type="Button" name="f" value="select at 5" visible="Yes" enabled="Yes"
   onclick='test.selectedIndex=4; alert("selected");'>

</cfform>

and an update on the illegal ActionScript error, you can't use any of the restricted keywords even in AS comments. "//new delete" will also throw that error.

August 28, 2005
serendipitous flash forms
as most of the coldfusion/arcIMS usergroup knows, i have a thing about xml. i don't know why (perhaps too many fast balls to the head, one too many falls off my mountain bike, too much tequila in college, who knows) but whenever i see any complex xml my brain just freezes. try as i might, i just can't seem to get my head around anything but the most simple xml. it got to the point where i ported a whole java library to CFCs just so i wouldn't have to use xml for some GIS applications.

i've been struggling for a week now trying to work with the xml that cftree needs if you use it via remoting (and who wouldn't these days with flash forms in cf7)--yes you have to feed it xml rather than a cfquery, cfform like cfdocument, hides rather a lot complexity from us poor coldfusiuon developers. one of the things i'm trying to do was locate a given node in a cftree. while plowing thru the flex documentation i noticed methods to determine if a node was a branch (had nodes under it) and if that branch was open or closed. what you can programatically get at depends on whether a branch is open or closed. don't take my word, put up a ctree w/a button that does a

onclick="alert(cftreeObj.length);"

and see what you get as you open and close nodes in the tree. to make a long story short, while trying to figure out a way to traverse a cftree i stumbled on a method to open or close a cftree via some simple ActionScript.

<cfsavecontent variable="open">
var theTree=theTree; //cftree name
var i=0;
// prime the pump
var thisNode=theTree.getTreeNodeAt(i);
while (thisNode != undefined){
   if (theTree.getIsBranch(thisNode) && ! theTree.getIsOpen(thisNode)){
         theTree.setIsOpen(thisNode,true);
      }
      i++;
      thisNode=theTree.getNodeDisplayedAt(i);
   }
</cfsavecontent>

<cfsavecontent variable="close">
var theTree=theTree; //cftree name
var i=0;
// prime the pump
var thisNode=theTree.getTreeNodeAt(i);
while (thisNode != undefined){
      if (theTree.getIsBranch(thisNode) && theTree.getIsOpen(thisNode)){
         theTree.setIsOpen(thisNode,false);
      }
i++;
thisNode=theTree.getNodeDisplayedAt(i);
   }
</cfsavecontent>

you can see a simple example here. i imagine you could adapt this to open/close any bits you needed.

August 20, 2005
thou shall not....
use illegal ActionScript (AS) in flash forms. i'm posting this as it might save someone else time and frustration. in order to keep a lid on how wild you can get w/the super cool flash forms in cf7 there are certain AS keywords which you aren't allowed to use (in any way, shape or form):

  • __proto__
  • import
  • createTextField
  • loadMovie
  • attachMovie
  • Delete
  • New
  • createChild
  • duplicateMovieClip
  • registerClass

this is all documented in Creating Forms in Macromedia Flash/Using ActionScript in Flash forms section of cfdocs. but what the docs aren't saying is that you can potentially use AS in some (to me) strange places. two of the strangest places are in alert message text and tooltips. the tooltips nearly drove me mad trying to figure out why i was getting illegal AS errors in my flash form when none of my injected AS was using any of the restricted keywords. it turns out i had a couple of buttons with the seemingly innocent "delete" word in their tooltips.

so, pay attention. if you're getting seemingly insane illegal AS errors, look to your tooltips and/or your alert messages instead of head-butting your monitor (or bombarding mike nimer with emails).

update: look to your injected ActionScript comments as well.

August 15, 2005
somewhat less cheesy flash forms trick
everybody's doing it. you know you want to. so why not go ahead and get on the flash forms bandwagon? it seems like pretty much every cf developer on the block has now developed a penchant for using cf7's new flash forms. quite a large part of this popularity is due to the ActionScript injection gravy that's been posted on AS Fusion, cf_pim and cfform. who would have thought a few lines of ActionScript would have breathed that kind of life into something as bland as a form?

one of the things that took some time for me to catch on to was that the ActionScript we were injecting into our flash forms, wasn't quite "live". for example, what would the following alert show if injected into a form button that some cranked up monkey was taught to push once a minute for an hour?

<cfsavecontent variable="testInc">
var x=1;
x=x+1;
alert(x.toString());
</cfsavecontent>

while i suppose pretty much everyone else knew the correct answer (2), it took me almost an hour to figure that out. if you wanted to have that var updated you'd need to store it's values outside that snippet (which is re-run exactly as is whenever that crazy monkey mashed down it's button), the usual suspect being one flavor or other of cfinput. even in moderately complex apps this inevitably leads to a plethora of cfinputs named (in our naming scheme) like cheesyFolderHolder, cheesyServerStatus, cheesyIMAPserver, cheesyGrabBag, etc. the relationships and interactions between these can get kind of hairy for a cf developer more used to the seemingly cleaner environment of the back-end (well it seemed kind of by-the-seat-of-your-pants to me anyway).

luckily one of the more interesting bits of ActionScript that cfform does allow is sharedObject (you flash developers can avert your eyes while i try to explain this to my fellow cf developers, it's kind of like a cookie, ok you flash developers can look now). so you can do things like:

<cfsavecontent variable="startUp">
// Create a local shared object
var so = SharedObject.getLocal("imapClient");
so.data.username="joe@blow.com";
so.data.password="yup, still crazy";
so.data.host="blow.com";
so.data.port=143;
so.data.currentFolder="INBOX";
so.data.getHeaders=false;
so.data.debug=false;
// write it out to the client
so.flush();
}   
</cfsavecontent>

which makes the info contained in the data part of that "imapClient" sharedObject available to other ActionScript snippets:

<cfsavecontent variable="checkEmail">
var imapObj=SharedObject.getLocal("imapClient");
var mailArgs = {
   username:imapObj.data.username,
   password:imapObj.data.password,
   imapHost:imapObj.data.host,
   folder:imapObj.data.currentFolder,
   getHeaders:imapObj.data.getHeaders,
   debug:imapObj.data.debug,
   allMessages:true};
.
.
.
//get service
imapService = connection.getService("cfc.remoteIMAPFacade",responseHandler);
//make call
imapService.fetchProfile(mailArgs);   
</cfsavecontent>

one downside is that users can turn this functionality off like cookies in a browser, so you'd need to test for this and fallback accordingly (though i think this means the data won't be saved locally but still available between snippets).

thanks to seth duffy for suggesting this idea.