Wednesday, February 10, 2010

ColdFusion 9: Yet Another Way to Refresh a Bound CFSELECT

One of the really nice features of CF8 and CF9 is bindings. But there are a few noticeable gaps, such as refreshing a bound CFSELECT list. From what I can tell, this still is not supported. Though apparently CF9 did introduce support for the selected attribute.


I spent some time pouring over the cfajax.js file, and had little more than a headache to show for my troubles. Then I came across a slick suggestion in an older entry on Todd Sharp's blog. It mentioned the undocumented ColdFusion.Bind.assignValue method could be used to refresh the list.  It was perfect.  Exactly what I needed.

Well... then I got to wondering if there might be something even simpler. (I know, I know. People are never satisfied).  After further review of cfajax.js, and lots of experimentation, I came across a nice one-liner that seems to work. Still undocumented unfortunately. But still pretty neat:

<script language="javascript">
    ColdFusion.bindHandlerCache['yourElementID'].call();
</script>

... and what does this function do?
If you generate a cfselect with a CFC bind, and view the source, you will see a call to a function named register(). Not surprisingly, the register() function seems to do all the work of creating the bindings and invoking the CFC to populate the select list.


After taking a peak inside cfajax.js, I noticed the register() function creates a parameter-less function which handles invoking the CFC. (You can open up cfajax.js yourself if you want to view all the gory details.) But the key part is that CF stores this handler function in one of its cache objects. So you can easily grab a reference to it function just by passing in the id (or name) of your form element. Then simply invoke it using call().

Pretty cool stuff!

...Read More

Tuesday, February 9, 2010

ColdFusion 9: CFSELECT + BIND + Selected Attribute Work Together Now?

In case you have been living under a rock like me, it seems the selected attribute works with bound cfselect lists.


Form

<cfparam name="form.foodID" default="0">

<cfform method="post" action="#CGI.SCRIPT_NAME#">
    Favorite Food: 
                <cfselect name="foodID"
                    value="foodID"
                    display="FoodName"
                    selected="#form.foodID#"
                    bind="cfc:Food.getFood()"
                    bindOnLoad="true"
                />
    <cfinput type="submit" name="submitMe" />
</cfform>

Food.cfc
<cfcomponent output="false">
   
   <cffunction name="getFood" access="remote" output="false" returntype="query">
      <cfset var q    = QueryNew("") />
      <cfset queryAddColumn(q, "FoodID", "integer", listToArray("0,1,2,3")) />
      <cfset queryAddColumn(q, "FoodName", "varchar", listToArray("-Pick Something-,Burger,Pizza,Lasagna")) />
      <cfreturn q />      
   </cffunction>
   
</cfcomponent>

...Read More

Sunday, February 7, 2010

ColdFusion 9: CFPDFFORM (A Tag With No Name ?)

A question about saving the output of cfpdfform to a variable came up on the adobe forums recently. As I have been doing some testing with CF9 + CFPDF this week, I suggested using the name attribute. Well I was a bit surprised when someone casually mentioned that cfpdfform does not have a documented name attribute. Huh?


Scratching my head I checked the documentation and sure enough name was not one of the listed attributes. It is documented for cfpdf, but not cfpdfform. So I re-checked my code and the name attribute did work in simple tests.

<cfpdfform action="populate" source="c:\MyForm.pdf" name="pdfData">
    <cfpdfformparam name="name" value="Mark McCallan">
    <cfpdfformparam name="address" value="926 Data Avenue">
</cfpdfform>

<cfcontent type="application/pdf" variable="#ToBinary(pdfData)#" />

So I started to wonder if maybe it was just an omission in the documentation. I decided to try the highly unscientific method of using invalid attribute combinations. Just to see if the error messages would shed any light on the expected attributes. Granted it is probably not the most reliable indicator. But worth a shot.


Unfortunately the messages for some combinations suggest "name" is valid


While others seemed to side with the documentation.


A dump of the tag's class certainly seems to suggest support for a "name" attribute.


I was leaning towards a fluke in the documentation. But then I noticed a few issues with a LiveCycle form that had special rights enabled. When the pdf opened, Acrobat complained that the document was modified and disabled the special features.


So now I am thinking maybe it was not an omission in the documentation. Anyone have the real scoop on the "name" attribute?

...Read More

Thursday, February 4, 2010

Form Field Values, Multipart Forms and Servlets (... oh my)

I saw an intriguing question on stackoverflow yesterday, by Tony Petruzzi .  It was about parsing form field values, but with a bit of twist. We all know that when multiple form fields have the same name, the field values are returned as a comma delimited list in CF. While that is a handy feature at times, what do you do when the field values contain commas? Well after a few false starts, I did come up with one option, but wanted to describe it in a little more detail than stackoverflow comments allow.

Stealing from Tony's example, take the simple form below. Each of the two fields contains a different number of letters separated by a comma. But once the form is submitted, the value is just one big csv string. So you really have no way of determining which values were actually entered into each of the fields.



Tony's post described a slick method of extracting the field values as a string array, with the help of getPageContext() and getParameterMap(). The problem was it does not seem to work when the enctype is multipart/form-data.


<cfdump var="#getPageContext().getRequest().getParameterMap()#">

From what little I have read so far, my understanding of the issue is that the standard request object (ie HttpServletRequest) does not handle multipart requests.  A special multipart handler is needed instead. So with that in mind, my next question was what handler does CF use?

Well, while poking around in the CF libraries a while back, I noticed CF uses some of the O'Reilly servlet classes. In particular, the ones for MultiPartRequest handling. If you look at the API you will notice the classes return all sorts of good information like input field names, values, etcetera. So I figured that is probably what CF was using for the form and that this information had to be exposed ...somewhere.

On a whim I decided to get back to basics and dump the FORM scope class. That revealed a promising method named getPartsArray(). Looping through the array confirmed my suspicion that results were indeed one of the O'Reilly classes:
com.oreilly.servlet.multipart.ParamPart


<cfset formClass = createObject("java", form.getClass().name)>
   <cfdump var="#formClass#" label="FORM Scope Class" />
   
   <cfloop array="#form.getPartsArray()#" index="part">
      <cfdump var="#part#" label="Part Element" />
   </cfloop>


All that was left was to use a few methods from the API to the retrieve the form field information. First the isParamPart() method is used to identify the input fields only. Then getName() and getStringValue() are used to grab the field's name and value.  As you can see, there is not much to it.

<!--- if this is a multipart request ...--->
   <cfset variables.parts = form.getPartsArray()>
   <cfif structKeyExists(variables, "parts")>
      <cfoutput>   
      <cfloop array="#variables.parts#" index="p">
         <cfif p.isParam()>
            isParam() = #p.isParam()#
            getName() = #p.getName()#
            stringValue() = #p.getStringValue()#
         </cfif>
      </cfloop>
      </cfoutput>
   </cfif>


Of course this only applies to multipart requests. The part array will be null for other request types. So make sure the array exists before using it. Anyway, I thought this was an interesting approach to the old form field dilemma. But if anyone knows of any other techniques, I would love to hear them.

...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep