Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 00:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/9753150 to your computer and use it in GitHub Desktop.
Save bennadel/9753150 to your computer and use it in GitHub Desktop.
Multi-Step Form Demo In ColdFusion
<cfcomponent
output="false"
hint="I configure the application.">
<!--- Define application settings. --->
<cfset THIS.Name = "MultiPartFormDemo" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 5, 0 ) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 5, 0 ) />
<!--- Define page settings. --->
<cfsetting showdebugoutput="false" />
<cffunction
name="OnSessionStart"
access="public"
returntype="void"
output="false"
hint="I fire when a session starts.">
<!---
Create a struct to hold the multi-paart form data.
Each set of form data will be stored in a unique ID.
--->
<cfset SESSION.FormData = {} />
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
<cfoutput>
<p>
Thanks for taking the time to fill out this form.
Please review the data below:
</p>
<ul>
<li>
<strong>Name:</strong>
#REQUEST.FormData.Name#
</li>
<li>
<strong>Birthday:</strong>
#REQUEST.FormData.Birthday#
</li>
</ul>
<input type="submit" value="Submit Data" />
</cfoutput>
<!---
In our index file, we are going to include an action
page and a display page for the master form. Each of
these files will take care of the sub-steps of our
multi-step form.
--->
<cfinclude template="_form_act.cfm" />
<cfinclude template="_form_dsp.cfm" />
<!---
Create an attributes scope to combine the form and url
data into a single scope.
--->
<cfset REQUEST.Attributes = Duplicate( URL ) />
<cfset StructAppend( REQUEST.Attributes, FORM ) />
<!---
Param the form-ID. This is the unique identifier to hold
the data for this multi-step form.
--->
<cfparam
name="REQUEST.Attributes.form_id"
type="string"
default=""
/>
<!--- Param the current step of the form process. --->
<cfparam
name="REQUEST.Attributes.step"
type="numeric"
default="1"
/>
<!--- Param the form submission flag. --->
<cfparam
name="REQUEST.Attributes.submitted"
type="boolean"
default="false"
/>
<!---
Check to see if we our current form-ID exists in the session
cache. If it does not, then we are hitting this multi-part
form for the first time.
--->
<cfif NOT StructKeyExists( SESSION.FormData, REQUEST.Attributes.form_id )>
<!---
Initializing the form data. Here, we don't need to
intialize the entire form data, just the missions
critical parts.
--->
<!--- Create a new ID. --->
<cfset REQUEST.Attributes.form_id = CreateUUID() />
<!---
Create a new struct to hold the form data. In this
struct, each step is going to be set to False; this
boolean will flag whether or not the given step has
been completed by the user.
--->
<cfset REQUEST.FormData = {
ID = REQUEST.Attributes.form_id,
Step1 = false,
Step2 = false,
Step3 = false,
Step = 1,
StepCount = 3
} />
<!---
Store the form data in the session cache using our
new UUID.
--->
<cfset SESSION.FormData[ REQUEST.FormData.ID ] = REQUEST.FormData />
</cfif>
<!---
ASSERT: At this point, whether this is a first run page or
a sub-step, our multi-part form data struct has been created
and cached in our SESSION data cache. It also contains all
the mission critical data for the process.
--->
<!--- Get the form data out of the SESSION cache. --->
<cfset REQUEST.FormData = SESSION.FormData[ REQUEST.Attributes.form_id ] />
<!---
Check to see if our current step is a valid step. It is
valid if it is an available step number AND that the
previous step was completed. If the previous step was NOT
completed, then the user is trying to skip ahead.
--->
<cfif (
(NOT ListFind( "1,2,3", REQUEST.Attributes.step )) OR
(
(REQUEST.Attributes.step GT 1) AND
(NOT REQUEST.FormData[ "Step#(REQUEST.Attributes.step - 1)#" ])
))>
<!---
The user has tried to access a step in the form that
does not exist or was not available (due to previous
step completion). Send them back to first step.
--->
<cfset REQUEST.FormData.Step = 1 />
<cfelse>
<!--- The step was valid, store it in the form data. --->
<cfset REQUEST.FormData.Step = REQUEST.Attributes.step />
</cfif>
<!---
Now that we have the step propertly evaluated, let's use the
step to update the rest of the form data. Everytime a user
goes to a given step, we want to make sure they have to work
their way BACK through the form. Therefore, step all forward-
facing steps (this one inclusive) to false.
--->
<cfloop
index="intStep"
from="#REQUEST.FormData.Step#"
to="#REQUEST.FormData.StepCount#"
step="1">
<!--- Set given step to false (not completed). --->
<cfset REQUEST.FormData[ "Step#intStep#" ] = false />
</cfloop>
<!--- Create an array to hold form data. --->
<cfset REQUEST.Errors = [] />
<!---
We have properly configured our FormData values. Include
the appropriate action file.
--->
<cfinclude template="_form_step#REQUEST.FormData.Step#_act.cfm" />
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Multi-Step Form Demo</title>
</head>
<body>
<cfoutput>
<h1>
Multi-Step Form Demo
</h1>
<!---
Output a link for each form step so users can jump
around (if they want to).
--->
<ol>
<cfloop
index="intStep"
from="1"
to="#REQUEST.FormData.StepCount#"
step="1">
<li>
<a href="./index.cfm?form_id=#REQUEST.FormData.ID#&step=#intStep#">Step #intStep#</a>
<cfif (REQUEST.FormData.Step EQ intStep)>
<strong>&laquo;</strong>
</cfif>
</li>
</cfloop>
</ol>
<form
action="#CGI.script_name#"
method="post">
<!---
Submit form definition data back with the form.
This will allow each form step to know where it
left off. This also means that each step MUST
submit the form data back to itself - you cannot
simply move a person to another step.
--->
<input
type="hidden"
name="form_id"
value="#REQUEST.FormData.ID#"
/>
<input
type="hidden"
name="step"
value="#REQUEST.FormData.Step#"
/>
<!--- Flag the form as being submitted. --->
<input
type="hidden"
name="submitted"
value="true"
/>
<!--- Check to see if there were any errors. --->
<cfif ArrayLen( REQUEST.Errors )>
<p>
Please review the following:
</p>
<ul>
<cfloop
index="strError"
array="#REQUEST.Errors#">
<li>
#strError#
</li>
</cfloop>
</ul>
</cfif>
<!--- Include the form step. --->
<cfinclude
template="_form_step#REQUEST.FormData.Step#_dsp.cfm"
/>
</form>
</cfoutput>
</body>
</html>
<!---
Param cached form data. We only need to cached the
form data that will be made available for this step.
--->
<cfparam
name="REQUEST.FormData.Name"
type="string"
default=""
/>
<!---
Param form / attribute data. As we param the form data, use
the values in the cached data (in case we are hitting this
step for a second time.
--->
<cfparam
name="FORM.name"
type="string"
default="#REQUEST.FormData.Name#"
/>
<!--- Check to see if form was submitted. --->
<cfif REQUEST.Attributes.submitted>
<!--- Validate form data. --->
<cfif NOT Len( FORM.name )>
<cfset ArrayAppend(
REQUEST.Errors,
"Please enter your name"
) />
</cfif>
<!--- Check to see if we have any errors. --->
<cfif NOT ArrayLen( REQUEST.Errors )>
<!--- Store the form data in our cache. --->
<cfset REQUEST.FormData.Name = FORM.name />
<!--- Flag this step as being completed. --->
<cfset REQUEST.FormData.Step1 = true />
<!--- Forward user to next step. --->
<cflocation
url="./index.cfm?form_id=#REQUEST.FormData.ID#&step=2"
addtoken="false"
/>
</cfif>
</cfif>
<cfoutput>
<label>
Name:
<input
type="text"
name="name"
value="#FORM.name#"
maxlength="30"
size="40"
/>
</label>
<br />
<br />
<input type="submit" value="Submit Form" />
</cfoutput>
<!---
Param cached form data. We only need to cached the
form data that will be made available for this step.
--->
<cfparam
name="REQUEST.FormData.Birthday"
type="string"
default=""
/>
<!---
Param form / attribute data. As we param the form data, use
the values in the cached data (in case we are hitting this
step for a second time.
--->
<cfparam
name="FORM.birthday"
type="string"
default="#REQUEST.FormData.Birthday#"
/>
<!--- Check to see if form was submitted. --->
<cfif REQUEST.Attributes.submitted>
<!--- Validate form data. --->
<cfif NOT IsDate( FORM.birthday )>
<cfset ArrayAppend(
REQUEST.Errors,
"Please enter your birthday"
) />
</cfif>
<!--- Check to see if we have any errors. --->
<cfif NOT ArrayLen( REQUEST.Errors )>
<!--- Store the form data in our cache. --->
<cfset REQUEST.FormData.Birthday = FORM.birthday />
<!--- Flag this step as being completed. --->
<cfset REQUEST.FormData.Step2 = true />
<!--- Forward user to next step. --->
<cflocation
url="./index.cfm?form_id=#REQUEST.FormData.ID#&step=3"
addtoken="false"
/>
</cfif>
</cfif>
<cfoutput>
<label>
Birthday:
<input
type="text"
name="birthday"
value="#FORM.birthday#"
maxlength="20"
size="20"
/>
</label>
<br />
<br />
<input type="submit" value="Submit Form" />
</cfoutput>
<!--- Check to see if form was submitted. --->
<cfif REQUEST.Attributes.submitted>
<!---
The user has confirmed that the data they have submitted
is correct. Now, do something with the data (ie. insert
into database) and send the user to a confirmation page.
Also, you can delete the FORM data from the SESSION if you
want to free up some memory.
ex.
StructDelete( SESSION, REQUEST.FormData.ID )
--->
<cflocation
url="./confirm.cfm"
addtoken="false"
/>
</cfif>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment