Getting meaningful data from dynamically generated fields in ColdFusion Flash Forms

Posted August 10, 2005 4:36 pm
Filed under: Articles by Paul, ColdFusion Flash Forms, Flash, Tutorials

Part 4 of My first foray into ColdFusion Flash Forms.

Note: This article will make more sense if you read the previous one first.

In the previous articles I talked about two approaches to creating a repeating set of form controls (in this case checkboxes) in a ColdFusion Flash Form, based on data in a query. I recommended one approach, which is to use a cfformgroup type="repeater" tag to create multiple controls, because it doesn’t require the SWF file to be regenerated each time the underlying data changes. The alternative, using a cfloop tag to create cfinput type="checkbox" controls, does require the SWF file to be regenerated each time the underlying data changes.

However, the cfloop approach has a big advantage. By creating the checkbox controls separately within your CFML, you can specify a separate name for each checkbox control, and it is easy to differentiate between them when the form is submitted.

Consider these two examples (which are the same as in the previous article, except that I have added a “submit” button to the form).

<cfform name="form1" format="flash" height="600" width="450">

	<cfformgroup type="repeater" query="q1">
		<cfinput type="Checkbox" name="chk" Label="{q1.currentItem.firstname + ' ' + q1.currentItem.lastname}">
	</cfformgroup>
	<cfformgroup type="horizontal">
		<cfinput type="submit" name="submitBtn" value="Send Data" width="100">
	</cfformgroup>
</cfform>

As described previously, this example uses a cfformgroup type="repeater", which dynamically generates the checkboxes on the client, using ActionScript in the SWF file.

<cfform name="form1" format="flash" height="400" width="450">
	<cfformgroup type="VBox">
		<cfloop query="q1">
			<cfinput type="checkbox" name="boxMBAa_#id#" label="#firstname# #lastname#">
		</cfloop>
	</cfformgroup>
	<cfformgroup type="horizontal">
		<cfinput type="submit" name="submitBtn" value="Send Data" width="100">
	</cfformgroup>
</cfform>

Again, the difference here is that a cfloop is used to generate the checkboxes in the server, so in the generated MXML (and the generated SWF file) they are separate checkbox controls.

When you submit these forms, the results are surprisingly different.

The output of the first example (using cfdump var="#form#") looks like this (assuming the first and the third checkboxes are checked):

Results of the first form.

While the output of the second example looks like this:

Results of the second form.

Obviously there is a significant difference. When we submit the first form, we only get one name/value pair containing the values for all the checkboxes. Not only that, but all we get for each checkbox is a true/false value. There is no practical way that we can figure out which associated value in our underlying data source was selected. However, with the second example, because each checkbox was created separately on the server (and we were able to give each one its own name) we see four different name/value pairs — one for each checkbox.

This is a serious problem for the cfformgroup type="repeater" approach to dynamically creating form controls. If there isn’t any way to get the data back, there isn’t really any point to creating the form a in the first place. I hoped that there would be a way to assign a “data” value to each checkbox which would get returned in place of the true/false value (in the same way that multiple checkboxes with the same name in an HTML form return a value). But there doesn’t seem to be a way to do this.

Unfortunately, I think this boils down to a limitation in the fact that the CF Flash Forms are using Flex but are submitting their data as if they were HTML forms. If we were actually building this same form using Flex, then there would be no need to get anything more than a true/false value from the checkbox. If we wanted to know which underlying values were selected, within our Flex application we could write code that would loop through the checkboxes to find out which ones are checked, and use that in combination with the underlying data (which would also be available to the ActionScript) — we could then manipulate that data, send it to the server, etc. however we like.

But that isn’t how CF Flash Forms work — they submit their data to the server as though it was from a traditional web form. Note that we could actually use an alternative approach (e.g. Flash Remoting) to submit our data, rather than using the standard “submit” behavior of the Flash Form — but that seems like a lot of extra work just so that we can have a dynamically generated set of form controls built the right way according to Macromedia’s recommendations.

However, I was able to come up with a workaround. Admittedly the data submitted by the form isn’t necessarily as clean and convenient as with the “checkboxes generated by cfloop” approach, but it at least makes it possible to get the data associated with checkboxes.

The solution: input type="hidden". As you probably know, within an HTML form you can use a “hidden” form field to store a value which gets submitted by the browser but which doesn’t have any visible manifestation on the page. With a CF Flash Form, you can add an input type="hidden" field to the form which has the same effect. So to get the id values associated with our checkboxes, we just need to create a hidden form field which contains all the id values in a comma separated list. Just add these lines to your code:

.
.
.
<cfparam name="mylist" default="">
<cfloop query="q1">
	<cfset mylist = listappend(mylist,#q1.id#)>
</cfloop>
.
.
.
<cfform name="form1" format="flash" height="600" width="450">
.
.
.
	<!--- the code for the repeating form controls (i.e. the cfformgroup type="repeater") goes here --->
	<cfinput type="hidden" name="ids" value="#mylist#">
</cfform>
.
.
.

Notice that outside the cfform tag we use cfloop to create a variable that is a ColdFusion “list” containing all the id values from our query. The contents of that variable are then set as the value of our hidden form field.

So now when we submit our form, we see results that look like this:

Results of the first form, with the addition of a hidden form field.

Notice the one (significant!) difference — there is now an extra name/value pair being submitted by our form, containing a comma-separated list of the ids from our query. Within the page that processes this form submission, we will have to loop through the two form fields (the one containing the true/false values and the other containing the ids) to match them up, in order to determine which values are the “selected” ones.

At this point, astute readers will probably be thinking “wait a minute, in the last article Paul said that it’s bad to use cfloop with CF Flash Forms, because the server has to regenerate the data each time the data changes. What gives?”

That was exactly my concern with using this approach. However, the CF engineers apparently anticipated this need (and here I have to give big kudos to those CF engineers, because I think what they’ve done is just brilliant). Take a look at the MXML generated when the <input type="hidden" /> is included. Try to find what the <input type="hidden" /> gets turned into in MXML. Give up? Look near the top for a tag named mx:Model. It looks like this:

<mx:Model id="form1">
	<chk>{chk.selected}</chk>
	<ids>{remoteObject_form1.getData.result.ids}</ids>
</mx:Model>

Notice that it contains two elements, which correspond exactly to the values that are submitted with the form. The <chk> is how the true/false values actually get put into the form field named “chk” that is submitted with the form. And there’s our hidden form field, in the <ids> tag. Notice something very important about it: it doesn’t contain a hard-coded list of values which would need to be recompiled each time. Rather, it contains a binding to an ActionScript value (the stuff in curly brackets {}). At the end of the previous article I pointed out that the “remoteObject_form1” is an object defined in the Flex MXML that essentially is how the SWF file gets the latest data each time it is loaded in the browser, without needing to recompile on the server. The value contained in the <ids> tag is a binding to the “ids” property of that same remote object. This means that each time the SWF file loads and gets the latest data from the server, it also gets the latest values to submit as the ids form field — but still without requiring the server to recompile the MXML.

Again, all I can say is that whoever wrote the code that is smart enough to turn my ColdFusion code (that I just wrote off pretty much off the top of my head)…

<cfparam name="mylist" default="">
<cfloop query="q1">
	<cfset mylist = listappend(mylist,#q1.id#)>
</cfloop>

…into this MXML…

<mx:Model id="form1">
.
.
.
<ids>{remoteObject_form1.getData.result.ids}</ids>
</mx:Model>

…is just brilliant.

(Well, either that or I just got really really lucky and happened to type just the right CF code that gets turned into that MXML. Okay, we all know how likely that is. =)

You can leave a comment, or trackback from your own site.

  • Michael Sewell

    Great article(s), since I ran into this exact issue last night. I want to be sure I am clear on one thing. As long as the underlying query data does not change, building a form using cfloop would NOT require the form to be rebuilt on the server. So the server can send the cached version without rebuilding.

  • http://probertson.com Paul Robertson

    I honestly don’t know for sure about that. I am certain that when the data returned by the query changes, CF will rebuild the form. If one person requests the page and it builds the form, then another person requests the page (and the data hasn’t changed in the mean time) does it rebuild the form/recompile the SWF again? I really don’t know.

    I guess it depends on whether/how CF is managing the caching. I suppose it’s possible that it compares the post-cfloop output (i.e. the stuff that gets turned into MXML, or else the actual MXML) and only recompiles if one of those has changed. Now that I think about it, it is probably likely that it only recompiles if the generated MXML has changed, since that’s how Flex works. But again, I can’t say for certain. If you know for sure then I would be interested to hear it.

  • felipe

    I may be saying bull____, but if coldfusion is able to compile a mxml code there may be some way to use it to create flex applications, without the flex server. Am I too far from reality?

    Edited slightly for language - PR

  • http://probertson.com/ Paul Robertson

    Felipe-
    You are absolutely right that ColdFusion is compiling MXML code, because ColdFusion MX 7 has Flex built into it.

    I don’t know all the details, of course, but both are written in Java, so I imagine that the Flex compiler is simply included as part of ColdFusion (maybe as a standalone file like with Flex, or maybe compiled into one of the ColdFusion files).

    So theoretically, assuming the flex compiler actually ships with CF as a separate file, you could use it to build Flex SWFs. Definitely goes against the license, I would imagine…

    More info on command-line Flex from:
    - Ted Patrick
    - Macromedia LiveDocs
    - Darron Schall

  • Michael Sewell

    I sent the question to Nimer, this is the response:

    As long as the generated mxml doesn’t change, by even one character, the form is the same - regardless of how it’s generated.

  • OneFineNight

    How can I dynamically change the data results of the repeater?

    i.e. the page load with today data as the default, but the users select yesterday view of the data.

  • Grant

    Thank you for the very helpful articles. I have one problem though:
    When I do a cfdump the data looks fine, but when I try to insert it into a database with this code:

    INSERT INTO testcheck (value)
    VALUES (‘#form#’)

    I get this error message: “Complex object types cannot be converted to simple values.”

    What can I do to fix this?

  • Josh

    Grant:

    Most likely you are passing a complex object type such as an array or a structure…. then your trying to pass it into some simple type like a string or a number…. make sure you convert your array to a string if thats what you are doing. I would look at your CF code. Sounds like thats the issue.

  • Kristin

    When using a repeater for checkboxes, it wasn’t too difficult to do a cfloop in the submission page to only insert the ones that have been checked. But what about doing my own validation in my own actionscript function before the form is submitted? Is there a way to see if at least one of the checkboxes has been checked, before the form has been submitted? I’ve tried looping through, but I’m not sure if all the checkboxes are created yet for the function to see. (btw, it was awesome of you to figure out this repeater in the first place, and post it for people like me to learn from :)

Articles by Type

Articles by Topic

Random Reading

Currently...

Adobe MAX 2011 Speaker H. Paul Robertson: Adobe Community Professional

Subscribe