While this is covered in the docs, it can be a bit surprising. When working with ColdFusion 8 Ajax-based containers (cfdiv, cflayoutarea, cfpod, and cfwindow), any form inside the area will be posted to the area itself. What that means is - the entire page won't reload, just the container. However - this is only true for cfform, not form. Consider this code example:
<cfoutput>loaded #timeFormat(now(), "long")#</cfoutput>
<p>
<hr>
<p>
<cflayout type="tab">
<cflayoutarea title="Tab 1">
<form>
<input type="text" name="name">
<input type="submit" name="submit">
</form>
<cfdump var="#form#">
</cflayoutarea>
<cflayoutarea title="Tab 2">
<cfform>
<cfinput type="text" name="name">
<cfinput type="submit" name="submit">
</cfform>
<cfdump var="#form#">
</cflayoutarea>
</cflayout>
I have a simple page with a time stamp on top. Then I have 2 tabs. The first tab has a FORM based form, and the second tab has a CFFORM based form. If you post the first tab, the entire page reloads. If you post the second form, the content of the second form is replaced.
And here is where things get freaky. Notice I didn't do an action for the cfform. That means the action is the same as the current page. The current page defines tabs. So guess what I get inside? Yep, tabs within tabs. Consider this screen shot:

Most likely this isn't what you want. You want to be sure you specify an action and that the action isn't the same as the page that hosts the form itself. So here is an example from an upcoming update to ColdFusionBloggers:
<cflayout type="tab" style="margin-left: 10px;">
<cflayoutarea title="Login" source="login.cfm" />
<cflayoutarea title="Register" source="register.cfm" />
</cflayout>
The contents of register.cfm contain just the form itself. (This page is in development right now, so pardon the half-completed error checking.)
<cfset errors = "">
<cfif structKeyExists(form, "selected") and form.selected is "register">
<cfif not len(trim(form.username))>
<cfset errors = errors & "You must enter a username.<br />">
<cfelseif reFind("[^a-z0-9]", form.username)>
<cfset errors = errors & "Usernames can contain only numbers and letters.<br />">
</cfif>
</cfif>
<cfoutput>
<p>
Note that all form fields are required.
</p>
<cfform action="register.cfm" method="post">
<p>
<label>Username</label>
<input name="username" value="" type="text" size="30" />
<label>Password</label>
<input name="password" value="" type="password" size="30" />
<label>Name</label>
<input name="name" value="" type="text" size="30" />
<label>Email Address</label>
<input name="email" value="" type="text" size="30" />
<br /><br />
<input class="button" type="submit" value="Login"/>
<input type="hidden" name="selected" value="register">
</p>
</cfform>
</cfoutput>
So all in all a pretty cool feature. I'll be able to reload inside my tab without reloading the entire page - but it is definitely something you have to watch out.
Archived Comments
Cool Ray, quick question, a little off topic but I want to move to a CF 8 server you have any suggestions?
Why not use the cfinput tags?
No reason. I don't normally use cfform as I don't like to rely on JS checking when I have to do server side anyway.
John, I used to use hosting.com, and was very happy with them. I have a new host now, but I don't name him as he isn't quite ready yet for new business.
I never liked the cfinput JS validations either. And now with CF8, you can write your server-side validation code in a CFC and leverage that same code client-side using cfajaxproxy, so you only have to maintain one set of validation scripts.
cool, when you don't specify it does cfform use the ColdFusion.Ajax.submitForm on its own?
I ask because another good 'oh yeah' is ColdFusion.Ajax.submitForm cannot handle file fields. I'm currently re-working some prototyping because of that :)
I haven't tried. You try and post back. :)
Hey Ray,
I'm trying to use a checkbox that submits using onclick inside the code you provide.
<cfinput type="checkbox" name="test" onClick="form.submit();" value="1">test
Unfortunately, the page reloads. How do you get it to load inside the tab?
JC: An explanation as to why that doesn't work:
http://cfsilence.com/blog/c...
Is it absolutely necessary to submit the form that way? It could be hacked but IMO it would get ugly.
Todd - Nope, not absolutely necessary, but something I stumbled across. Thanks for the detailed explanation.
I also have a CFC with a function that contains dynamically generated JScript. If I use it in an AJAX container, it fails miserably, too.
Gotta keep in mind that those containers are limited and keep it simple.
I'm having a little trouble figuring out navigation after submitting a form. usually, the page I start with stores a variable in the session scope (#Session.Source#) so that when the processing page is done it knows where to return me. I have a "content" cfdiv that has a form where I choose a customer, when I submit that the processing page checks to see if that customer has more than one business unit. If they do, I use a cflocation to go to go to the business unit form. So far so good, still in the same cfdiv content container. But when I submit that form to its processing page and it uses the same cflocation URL="#Session.Source#") to return me whence I came the entire page is replaced instead of the cfdiv content. so my question is, how do I need to manipulate the #Session.Source# variable to always update the cfdiv? I tried using <cfset Session.Source = "javascript:ColdFusion.navigate('/Dashboard.cfm','content');">
but cflocation doesn't seem to like that. maybe the syntax is wrong or maybe there's another way besides cflocation to send me on my way after the processing is done.
I read your comment twice and I don't get it. You said it works - then you said it didn't. Can you explain this again? I can't figure out the difference between your first case (which works) and your second case (which does not work).
And yes - you can't put anything but a valid URL (absolute or relative) in a cflocation.
Also - don't forget you can just post the form using ColdFusion.Ajax.SubmitForm(). No need to go anywhere really.
the difference is whether there is a second level. if a customer has more than one business unit, I cflocation to the business unit form which only replaces the cfdiv contents (which is as expected), but when i return from the second form, the cflocation url="#Session.Source#" replaces the whole page. If the customer has only one business unit, i never go to the business unit form and the cflocation url="#Session.Source" returns me where I was and only replaces the cfdiv content (not the whole page)
SO let me see if I get this right. In your div you load foo.cfm lets say. If foo.cfm sees you have one BU, it uses cflocation to send you to edit.cfm, which works fine.
If you have N BUs, you cflocate to a page where you ask a person to pick the BU. When they leave THAT page, you break out of the cfdiv.
SO my question is - on the page that displays the BUs, how do you let them pick a BU? Is it an HTML link? If so - you need to use AjaxLink to keep stuff inside the div. If it is a form, you need to use cfform to keep them in the div.
I start at the dashboard. the layout has a cfdiv for cfmenu, cfdiv for header, cfdiv for content. I choose change customer from the cfmenu and the ColdFusion.navigate updates the content cfdiv with the customer form with a cfselect to choose customer, that submits to processing page which saves the selection and counts bu for that customer; if >1 cflocation to bu form with cfselect. this submits to processing page, then cflocation to #Session.Source# to go back to original dashboard (hopefully all within the content cfdiv
Ok but in your "bu form with cfselect", are you sure it is "cfselect" and therefore a cfform?
that is all I ever use: cfform, cfinput, cfselect, etc.
on the cfmenu I also have a "Change Business Unit" menuitem that takes me directly to the BU form. I can change BU and come back to the dashboard all within the cfdiv. same BU form, just skip the customer form.
Truly odd. So your first form works, your second form does not. All I can suggest is poring over that form and being super-anal to make sure you aren't missing something obvious like a target= on the form.
Also consider maybe temporarily removing the form and replacing it with a ajaxlink to see if a GET op works ok. (ie, hard coded the BU)
Lastly, you can use COldFusion.Ajax.submitForm instead as I mentioned earlier.
supposedly you can use ColdFusion.navigate to submit a form and direct the results to a container but I can't find any examples and I can't get that to work either.
Here is another gotcha... suppose you have a form with three submit buttons on a cfform in a cfdiv named deleteMe,cancelMe,submitMe. if you make a change on the form and and click the "submitMe" button, which button gets saved to the form scope? well if you have a plain cfform and it's not part of a cfdiv layout, you get submitMe, but no matter which button I pressed on my cfdiv form I got deleteMe. the delete button was first on the form and it was inside a cfif tag to test if it was a new record or an edit. if it was a new record, the deleteMe button didn't exist so when I dumped the form I got cancelMe.
I found a little thing buried in the dev guide that said when you are using a javascript function in a dynamic page like a cfdiv that you ColdFusion.navigate, you have to construct your javascript function declaration a little backwards. All the examples I was looking at had the common syntax: function functionName(arguments){code} but in my case I needed to do something like functionName = function(arguments){code}. now I'm able to get the ColdFusion.Ajax.submitForm and ColdFusion.naviagate to work in javascript functions.
I wouldn't say "in a dynamic page" - but more, if you are writing JS code in a file that will be included via Ajax by another CF control, then you need ot use that format. I've blogged about this already I think. :)
Has anyone discovered a way to DISABLE the feature that forces a CFFORM to submit an Ajax request when used inside a CFDIV or CFLAYOUTAREA? I've run into multiple situations now where I do not want the submit/results to stay within the DIV or tab, especially with a CFDIV. Other than changing the CFFORM to a FORM, and writing all my own validation, I can't find a way to stop it!
Not that I know of - but you can use onSubmit and do the POST in the outer window. That should work.
This is a total hack job but, to get the select submit the form within the cflayout I used this on the select
onchange="document.yourFormID.yourSub...();"
and I made my submit button hidden
input type="submit" name="yourSubmitID" id="yourSubmitID" value="Submit" style="visibility:hidden"
Is there a better way to submit a form on event without actually clicking a submit button?
Are you able to set/reset your focus with this form inside a cflayout?
Hi Ray,
Quick question regarding session variables on CFPOD.
1. I have a button that grabs the of a selected CFGRIDROW.-
2. The button execute an onclick function, and the function looks like this:
x = ColdFusion.getElementValue('FileGrid','myGrid','Directory');
ColdFusion.navigate('test_LoadFile.cfm?Directory=' + x + '&' + 'Name=' + y + '&' + 'Path=' + z,'DevContainer');
Here's my issue:
1. I tried to create a session table inside 'test_LoadFIle.cfm'. The code somewhat looks like this:
<cfif isDefined('session.PlaceHolder')>
<cfset QueryAddRow(session.PlaceHolder,1)/>
<cfset QuerySetCell(session.PlaceHolder,"name","#url.name#")/>
..... (and some other querySetCell)
<cfelse>
<cfset session.PlaceHolder = QueryNew("name,path,directory")>
<cfset QueryAddRow(session.PlaceHolder,1)/>
<cfset QuerySetCell(session.PlaceHolder,"name","#url.name#")/>
..... (and some other querySetCell)
</cfif>
2. It looks like the 'session' table gets lost when I pick another value from my grid and add it to my session table. It will always execute the condition after <cfelse> in test_LoadFIle.cfm
I tried to create the session table in the main page, before CFPOD gets created. Same deal. It looks like everytime you use ColdFusion.navigate, it breaks the session.
Question is:
Do you know if there is any issue of session varaible break when using ColdFusion.navigate?
Thanks.
Melvin
Did you perhaps disable cookies in your app? That is the only way I can think of the session being recreated. What happens if you use onSessionStart in your Application.cfc to set some random #? Do that, output it in test_loadFile, and see if it is changing.
Hi Ray,
Your reply lead me to the solution to my issue. What I was doing initially was that I have a testing folder with all of my experiments. And this has no APPLICATION.CFC... jeeeeez!
So I created one, and tested it back. Session variables are retained using ColdFusion.navigate.
Thanks for the help.
Hey Ray,
Can you use <cfinput type=file> inside a cfpod? After posting, the file element is never in the form variable.
Thanks!
You can't - because cfpod loads it's content via Ajax, and you can't do (regular) file uploads via Ajax. Remember, all forms inside Ajax UI items like that try to post via Ajax, not a normal HTTP POST.
Well if I remember right, all CFFORMs do. A regular <form> may post to the entire window.
I know this is a very dated response/question to this thread, but I am currently using CF8 and having a little trouble in a similar area as this topic covers.
Is it possible to have a CFFORM in one CFDIV but have it target a different CFDIV?
Basically I have a search form in the top CFDIV where people can input various criteria in various categories. I want the results of the search to show in the bottom CFDIV. I could get a similar effect by just processing the whole page, but I thought this would be slicker for users.
I have tried various ways to bind and then send the data using ColdFusion.Navigate to the second CFDIV, but none of those attempts have been successful thus far.
Thanks for any help or insight you could provide.
_I have access to CF9 if that version may have better functionality for this._
First: Please note that I *strongly* urge you to move away from cfdiv and cfform. That being said - here is an idea.
Simply remove the submit button from the top form, and make your second cfdiv bound to the form fields of the first. See the form binding stuff in the docs for more info.