facebook google plus twitter
Webucator's Free ColdFusion MX Tutorial

Lesson: Flow Control

Welcome to our free ColdFusion MX tutorial. This tutorial is based on Webucator's Comprehensive ColdFusion Training course.

Lesson Goals

  • To work with <cfif><cfelseif><cfelse> blocks.
  • To work with <cfswitch><cfcase> blocks.
  • To create a self-processing form page.
  • To use loops in ColdFusion.
  • To set defaults for variables.
  • To use <cflocation> to redirect pages.

Conditional Processing

Conditional processing allows programmers to output different code based on specific conditions. There are two conditional structures in ColdFusion <cfif> blocks and <cfswitch> blocks.

If-Else if-Else Blocks

Simple <cfif>

The syntax for a simple <cfif> condition is shown below:

<cfif conditions> Hello World! </cfif>

In the above code, Hello World will be displayed only if the conditions are true.

<cfif><cfelse></cfif>

The syntax for a simple <cfif><cfelse> condition is shown below:

<cfif conditions> Hello World! <cfelse> Goodbye World! </cfif>

<cfif><cfelseif><cfelse></cfif>

The syntax for a simple <cfif><cfelse><cfelseif> condition is shown below:

<cfif conditions> Hello World! <cfelseif conditions> World, are you there? <cfelse> Goodbye World! </cfif>

There can be an unlimited number of <cfelseif> blocks, but a maximum of one <cfelse> block.

Switch Blocks

A <cfswitch> statement is similar to a <cfif> statement, except that it can only check for an equality comparison of a single expression. It cannot, for example, be used to check if one value is higher than another.

<cfswitch expression="#language#"> <cfcase value="German"> Hallo </cfcase> <cfcase value="Spanish"> Hola </cfcase> <cfcase value="French"> Bonjour </cfcase> <cfdefaultcase> Hello </cfdefaultcase> </cfswitch>

Decision and Boolean Operators

ColdFusion use words rather than symbols for its decision and boolean operators.

Operator Type Operator Description
Decision
IS
EQ
EQUAL
Returns True if the values are equal.
IS NOT
NEQ
NOT EQUAL
Returns True if the values are not equal.
GREATER THAN
GT
Return True if the value on the left is greater than the value on the right.
LESS THAN
LT
Return True if the value on the right is greater than the value on the left.
GREATER THAN OR EQUAL TO
GTE
GE
Returns True if the value on the left is greater than or equal to the value on the right.
LESS THAN OR EQUAL TO
LTE
LE
Returns True if the value on the right is greater than or equal to the value on the left.
CONTAINS
Returns True if the value on the left contains the value on the right.
DOES NOT CONTAIN
Returns True if the value on the left does not contain the value on the right.
Boolean
NOT
Reverses the value of an argument.
AND
Returns True if both arguments are True.
OR
Returns True if either argument is True.

Short-circuiting

ColdFusion stops evaluating a Boolean condition as soon as it knows the result. For example, when checking to see if "a AND b" is true, ColdFusion will only look at b if a is true, because if a is false then the whole expression is also false, regardless of the value of b. Likewise, when checking to see if "a OR b" is true, ColdFusion will only look at b if a is false, because if a is true then the whole expression is also true, regardless of the value of b. This behavior is called short-circuiting.

Condition Examples

Following are some examples of if-else if-else and switch-case expressions.

Code Sample:

FlowControl/Demos/If.cfm
<html>
<head>
<title>If-Else If-Else</title>
</head>
<body>
<cfset Age = 21>
<cfif Age GTE 21>
	You can vote and drink!
<cfelseif Age GTE 18>
	You can vote, but can't drink.
<cfelse>
	You cannot vote or drink.
</cfif>
</body>
</html>

The file is relatively simple. You can see the different results by changing the value of Age.

Code Sample:

FlowControl/Demos/If2.cfm
<html>
<head>
<title>If-Else If-Else - Compound</title>
</head>
<body>
<cfset Age = 21>
<cfset Citizen = false>
<cfif Age GTE 21 AND NOT Citizen>
	You can drink but you can't vote!
<cfelseif Age GTE 21>
	You can vote and drink!
<cfelseif Age GTE 18 AND Citizen>
	You can vote but can't drink!
<cfelse>
	You cannot vote or drink.
</cfif>
</body>
</html>

The example above shows the use of compound if conditions using boolean operators.

Code Sample:

FlowControl/Demos/Switch.cfm
<html>
<head>
<title>Switch-Case</title>
</head>
<body>
<cfset Quantity = 1>
<cfswitch expression="#Quantity#">
	<cfcase value="1">
		Quantity is 1
	</cfcase>
	<cfcase value="2">
		Quantity is 2
	</cfcase>
	<cfdefaultcase>
		Quantity is not 1 or 2
	</cfdefaultcase>
</cfswitch>
</body>
</html>

In many languages, a break is needed to end switch-case processing. In ColdFusion, this is not the case. As soon as the expression is matched, that case block code is executed, and the switch-case block exits.

Redirection Using <cflocation>

The <cflocation> tag is used to redirect the user to a different page. It is commonly used when a page has been moved or when a user doesn't have permissions to view a requested page. The syntax is as follows:

<cflocation url="some_url">

For example:

<cflocation url="http://www.webucator.com">

Form Validation

Duration: 20 to 30 minutes.

In this exercise, you will modify the calculator processing page so that it validates the form input. If there are no errors, the page will be processed. However, if errors are found, the user will be returned to the form page.

We will also be checking the units to see if the speeds are in mph or kph.

  1. Open FlowControl/Exercises/ProcessCalculator.cfm in your editor.
  2. At the very top of the page, write an if condition that checks to make sure that:
    • Neither of the name form fields is empty.
    • The speeds entered are numeric.
  3. If there are errors, redirect the page back to Calculator.cfm. Otherwise, continue with the page processing.
  4. Define a variable Conversion with the value of .62.
  5. Write an if condition that checks to see if the speeds are in mph or kph.
    • If they are in kph, yourmarathontime and friendmarathontime will have to be calculated using the Conversion variable.
  6. To test your new page, open FlowControl/Exercises/Calculator.cfm in your browser and submit the form.

Code Sample:

FlowControl/Exercises/ProcessCalculator.cfm
               <!---
	Write an if condition that checks to make sure that:
		A. Neither of the name form fields is empty.
		B. The speeds entered are numeric.
	If there are errors, redirect back to the FORM.
	Otherwise, continue with the page processing.
--->
<html>
<head>
	<title>Calculate Speeds</title>
</head>
<body>
<h1>Calculate Speeds</h1>

<cfset Marathon = 26.2>
<!---
	Define a variable Conversion with the value of .62

	Write an if condition that checks to see if the speeds
	are in mph or kph. If they are in kph, yourmarathontime
	and friendmarathontime will have to be calculated
	using the Conversion variable.
--->
<cfset yourmarathontime = Marathon/FORM.yourspeed>
<cfset friendmarathontime = Marathon/FORM.friendspeed>

<cfset yourhours = Int(yourmarathontime)>
<cfset yourminutes = (yourmarathontime * 60) MOD 60>
<cfset friendhours = Int(friendmarathontime)>
<cfset friendminutes = (friendmarathontime * 60) MOD 60>

<cfoutput>
#FORM.yourname#, at #FORM.yourspeed# #FORM.units#,
you would run a marathon in #yourhours#
hours and #yourminutes# minutes.<br>

At #FORM.friendspeed# #FORM.units#, #FORM.friendname# would
run a marathon in #friendhours# hours
and #friendminutes# minutes.<br>

<p>Form processed at #TimeFormat(Now(),"h:mmtt")# on
	#DateFormat(Now(),"mmmm d, yyyy")#.</p>
</cfoutput>
</body>
</html>

Add a basic error message to Calculator.cfm. To do this, you will need to figure out a way to pass data from ProcessCalculator.cfm to Calculator.cfm letting it know that there has been an error.

Solution:

FlowControl/Solutions/ProcessCalculator.cfm
               <cfif FORM.yourname EQ "" 
	OR FORM.friendname EQ ""
	OR NOT isNumeric(FORM.yourspeed) 
	OR NOT isNumeric(FORM.friendspeed)>
		<cflocation url="Calculator.cfm" addtoken="no">
</cfif>
<html>
<head>
	<title>Calculate Speeds</title>
</head>
<body>
<h1>Calculate Speeds</h1>

<cfset Marathon = 26.2>
<cfset Conversion = .62>

               <cfif FORM.units EQ "mph">
	<cfset yourmarathontime = Marathon/FORM.yourspeed>
	<cfset friendmarathontime = Marathon/FORM.friendspeed>
<cfelse>
	<cfset yourmarathontime = Marathon/Conversion/FORM.yourspeed>
	<cfset friendmarathontime = Marathon/Conversion/FORM.friendspeed>
</cfif>

<cfset yourhours = Int(yourmarathontime)>
<cfset yourminutes = (yourmarathontime * 60) MOD 60>
<cfset friendhours = Int(friendmarathontime)>
<cfset friendminutes = (friendmarathontime * 60) MOD 60>

<cfoutput>
#FORM.yourname#, at #FORM.yourspeed# #FORM.units#,
you would run a marathon in #yourhours#
hours and #yourminutes# minutes.<br>

At #FORM.friendspeed# #FORM.units#, #FORM.friendname# would
run a marathon in #friendhours# hours
and #friendminutes# minutes.<br>

<p>Form processed at #TimeFormat(Now(),"h:mmtt")# on
	#DateFormat(Now(),"mmmm d, yyyy")#.</p>
</cfoutput>
</body>
</html>

Challenge Solution:

FlowControl/Solutions/Calculator-challenge.cfm
<html>
<head>
<title>Marathon Time Calculator</title>
</head>
<body>

<h1>Marathon Time Calculator</h1>
<cfif isDefined("URL.error") AND URL.error EQ 1>
	<h2 style="color:red">You must fill out your name and friend's name!</h2>
<cfelseif isDefined("URL.error") AND URL.error EQ 2>
	<h2 style="color:red">The speeds must be numeric!</h2>
</cfif>
---- C O D E   O M I T T E D ----

Challenge Solution:

FlowControl/Solutions/ProcessCalculator-challenge.cfm
               <cfif FORM.yourname EQ "" OR FORM.friendname EQ "">
	<cflocation url="Calculator.cfm?error=1" addtoken="no">
<cfelseif NOT isNumeric(FORM.yourspeed) OR NOT isNumeric(FORM.friendspeed)>
	<cflocation url="Calculator.cfm?error=2" addtoken="no">
</cfif>
<html>
<head>
	<title>Calculate Speeds</title>
</head>
---- C O D E   O M I T T E D ----

isDefined() and <cfparam>

ColdFusion errors when it is asked to try to evaluate an undefined variable. In some cases, you will want to check to see whether a variable is defined before trying to evaluate it. You can use the isDefined() function to check this.

<cfif isDefined("Form.firstname")> <cfoutput>Hello, #Form.firstname#!</cfoutput> <cfelse> Hello, stranger! </cfif>

In other cases, you will want to set a default value for a variable in case it is accessed before it is explicitly defined. Use <cfparam> for this.

<cfparam name="Form.firstname" default="stranger"> <cfoutput>Hello, #Form.firstname#!</cfoutput>

In the example above, if Form.firstname is defined before the <cfparam> tag is reached, then the <cfparam> tag would be ignored. Otherwise, the default value of stranger would be assigned to Form.firstname.

Self-submitting Form

Duration: 20 to 30 minutes.

In the solution in the last exercise, when there is an error in the form, all the form data gets lost. This is not ideal. In this exercise, you will create a page that contains both the form and the processing code. We use the CGI.SCRIPT_NAME variable, which holds the name of the current file, as our form action. The page works as follows:

If the form has not yet been submitted (e.g, on the first visit to the page), the page outputs the form.

Likewise, if the form has been submitted with errors, the page re-outputs the form, but keeps any valid values that were entered.

If the form has been submitted with no errors, we'll process the form data and output the form showing the entered valued.

  1. Open FlowControl/Exercises/CalculatorSelfProcess.cfm in your editor.
  2. Much of the code is already written. Review it to make sure you have a good understanding of what's going on.
    • Notice the hidden field named "submitted" You can use this field to check to see if the form has been submitted.
  3. In the comments of the code you will find three tasks to complete.
  4. After completing the tasks, open FlowControl/Exercises/CalculatorSelfProcess.cfm in your browser and test your solution.

Code Sample:

FlowControl/Exercises/CalculatorSelfProcess.cfm
<html>
<head>
<title>Marathon Time Calculator</title>
</head>
<body> 
	<h1>Marathon Time Calculator</h1> 
	<!---
	TASK 1: Write if condition that checks if form was
	submitted and that there were no errors.
	---> 
	<cfif WRITE_CONDITION_HERE>
		<!---Set Variables---> 
		<cfset Marathon = 26.2> 
		<cfset Conversion = .62> 
		<!---Check units---> 
		<cfif FORM.units EQ "mph"> 
			<!---Specify which option should be selected---> 
			<cfset mphselected=" selected"> 
			<cfset kphselected=""> 
			<cfset yourmarathontime = Marathon/FORM.yourspeed> 
			<cfset friendmarathontime = Marathon/FORM.friendspeed> 
			<cfelse> 
			<!---Specify which option should be selected---> 
			<cfset mphselected=""> 
			<cfset kphselected=" selected"> 
			<cfset yourmarathontime = Marathon/Conversion/FORM.yourspeed> 
			<cfset friendmarathontime = Marathon/Conversion/FORM.friendspeed> 
		</cfif> 
		<cfset yourhours = Int(yourmarathontime)> 
		<cfset yourminutes = (yourmarathontime * 60) MOD 60> 
		<cfset friendhours = Int(friendmarathontime)> 
		<cfset friendminutes = (friendmarathontime * 60) MOD 60> 
		<cfoutput> #FORM.yourname#, at #FORM.yourspeed# #FORM.units#, you would run a marathon in #yourhours# hours and #yourminutes# minutes.<br> 
			At #FORM.friendspeed# #FORM.units#, #FORM.friendname# would run a marathon in #friendhours# hours and #friendminutes# minutes.<br> 
			<!---
			TASK 2: Write a nested if condition that determines
			who will win and by how many minutes.  The output to the
			browser should read:
			"You would beat Marylin by 10.3 minutes." OR
			"Marylin would beat you by 10.3 minutes." OR
			"Marylin and you would finish at the same time."
			---> 
			<p>Form processed at #TimeFormat(Now(),"h:mmtt")# on #DateFormat(Now(),"mmmm d, yyyy")#.</p> 
		</cfoutput> 
		<cfelseif isDefined("FORM.submitted")> 
		<!---Checks if form was submitted with errors.---> 
		<p class="errors">All fields are required.</p> 
		<p class="errors">Speeds must be numeric.</p> 
	</cfif> 
			<!---
			TASK 3: Use <cfparam> to create default values
			for the Form variables of empty strings.
			Replace all instances of "FOO" in the form below
			with appropriate values.
			You will also need to create default values for
			mphselected and kphselected.
			--->
	<cfoutput> 
		<form method="post" action="#CGI.SCRIPT_NAME#"> 
			<input type="hidden" name="submitted" value="true">
			<table> 
				<tr> 
					<td>Your Name:</td> 
					<td><input name="yourname" value="FOO"
					type="text" size="30" maxlength="50"></td> 
				</tr> 
				<tr> 
					<td>Your Speed:</td> 
					<td> <input name="yourspeed" value="FOO"
								type="text" size="4" maxlength="5"> </td> 
				</tr> 
				<tr valign="top"> 
					<td>Friend's Name:</td> 
					<td><input name="friendname" value="FOO"
								type="text" size="30" maxlength="50"> </td> 
				</tr> 
				<tr> 
					<td>Friend's Speed:</td> 
					<td> <input name="friendspeed" value="FOO"
								type="text" size="4" maxlength="5"> </td> 
				</tr> 
				<tr> 
					<td>Units</td> 
					<td> <select name="units"> 
							<option value="mph"#mphselected#>MPH</option> 
							<option value="kph"#kphselected#>KPH</option> 
						</select> </td> 
				</tr> 
				<tr> 
					<td colspan="2" align="right"> <input type="submit" value="Calculate"> </td> 
				</tr> 
			</table> 
		</form> 
	</cfoutput>
</body>
</html>

In your solution, the user probably gets no feedback if he submits the form with errors. Add an <cfelseif> block to the outer <cfif> block that checks to see if the form was submitted with errors and gives the user feedback if it was.

Solution:

FlowControl/Solutions/CalculatorSelfProcess.cfm
<html>
<head>
<title>Marathon Time Calculator</title>
</head>

<body>

<h1>Marathon Time Calculator</h1>

<cfif 	isDefined("FORM.submitted") AND
			( FORM.yourname NEQ ""
			AND FORM.friendname NEQ ""
			AND isNumeric(FORM.yourspeed)
			AND isNumeric(FORM.friendspeed) )>
			
	<!---Set Variables--->
	<cfset Marathon = 26.2>
	<cfset Conversion = .62>
	
	<!---Check units--->
	<cfif FORM.units EQ "mph">
		<!---Specify which option should be selected--->
		<cfset mphselected=" selected">
		<cfset kphselected="">
		<cfset yourmarathontime = Marathon/FORM.yourspeed>
		<cfset friendmarathontime = Marathon/FORM.friendspeed>
	<cfelse>
		<!---Specify which option should be selected--->
		<cfset mphselected="">
		<cfset kphselected=" selected">
		<cfset yourmarathontime = Marathon/Conversion/FORM.yourspeed>
		<cfset friendmarathontime = Marathon/Conversion/FORM.friendspeed>
	</cfif>
	
	<cfset yourhours = Int(yourmarathontime)>
	<cfset yourminutes = (yourmarathontime * 60) MOD 60>
	<cfset friendhours = Int(friendmarathontime)>
	<cfset friendminutes = (friendmarathontime * 60) MOD 60>
	
	<cfoutput>
	#FORM.yourname#, at #FORM.yourspeed# #FORM.units#,
	you would run a marathon in #yourhours#
	hours and #yourminutes# minutes.<br>
	
	At #FORM.friendspeed# #FORM.units#, #FORM.friendname# would
	run a marathon in #friendhours# hours
	and #friendminutes# minutes.<br>
	
	<cfif yourmarathontime LT friendmarathontime>
		<cfset diff = (friendmarathontime - yourmarathontime) * 60>
		You would beat #FORM.friendname# by #Int(diff)# minutes.
	<cfelseif friendmarathontime LT yourmarathontime>
		<cfset diff = (yourmarathontime - friendmarathontime) * 60>
		#FORM.friendname# would beat you by #Int(diff)# minutes.
	<cfelse>
		#FORM.friendname# and you would finish at the same time.
	</cfif>
	
	<p>Form processed at #TimeFormat(Now(),"h:mmtt")# on
		#DateFormat(Now(),"mmmm d, yyyy")#.</p>
	</cfoutput>
	
</cfif>

<cfparam name="FORM.yourname" default="">
<cfparam name="FORM.friendname" default="">
<cfparam name="FORM.yourspeed" default="">
<cfparam name="FORM.friendspeed" default="">
<cfparam name="mphselected" default=" selected">
<cfparam name="kphselected" default="">

<cfoutput>
<form method="post" action="#CGI.SCRIPT_NAME#">
<input type="hidden" name="submitted" value="true">
<table>
<tr>
	<td>Your Name:</td>
	<td><input name="yourname" value="#FORM.yourname#"
					type="text" size="30" maxlength="50"></td>
</tr>
<tr>
	<td>Your Speed:</td>
	<td>
		<input name="yourspeed" value="#FORM.yourspeed#"
								type="text" size="4" maxlength="5">
	</td>
</tr>
<tr valign="top">
	<td>Friend's Name:</td>
	<td><input name="friendname" value="#FORM.friendname#"
								type="text" size="30" maxlength="50">
	</td>
</tr>
<tr>
	<td>Friend's Speed:</td>
	<td>
		<input name="friendspeed" value="#FORM.friendspeed#"
								type="text" size="4" maxlength="5">
	</td>
</tr>
<tr>
	<td>Units</td>
	<td>
		<select name="units">
			<option value="mph"#mphselected#>MPH</option>
			<option value="kph"#kphselected#>KPH</option>
		</select>
	</td>
</tr>
<tr>
	<td colspan="2" align="right">
		<input type="submit" value="Calculate">
	</td>
</tr>
</table>
</form>
</cfoutput>
</body>
</html>

Challenge Solution:

FlowControl/Solutions/CalculatorSelfProcess-challenge.cfm
<html>
<head>
<title>Marathon Time Calculator</title>
</head>

<body>

<h1>Marathon Time Calculator</h1>

<cfif isDefined("FORM.submitted") AND
			( FORM.yourname NEQ ""
			AND FORM.friendname NEQ ""
			AND isNumeric(FORM.yourspeed)
			AND isNumeric(FORM.friendspeed) )>
---- C O D E   O M I T T E D ----
<!---CHALLENGE SOLUTION--->
<cfelseif isDefined("FORM.submitted")>
	<!---Checks if form was submitted with errors.--->
	<p class="errors">All fields are required.</p>
	<p class="errors">Speeds must be numeric.</p>
<!---END CHALLENGE SOLUTION--->
	
</cfif>
---- C O D E   O M I T T E D ----

Loops

As the name implies, loops are used to loop (or iterate) over code blocks. The following section shows the syntax for some of the different types of loops. All of these loops can be found in FlowControl/Demos/Loops.cfm.

Index Loops

An index loop is like a for loop in other languages. It steps through a process based on some type of count.

<cfloop index="count" from="1" to="5" step="2"> <cfoutput>#count#</cfoutput> </cfloop>

This loop will output 1 3 5.

Conditional Loops

A conditional loop is like a while loop in other languages. It continues to iterate through a process as long as the specified condition is true.

<cfset count=1> <cfloop condition="count LTE 5"> <cfoutput>#count#</cfoutput> <cfset count = count + 2> </cfloop>

This loop will output 1 3 5.

List Loops

Lists are simply strings delimited by a specified character or set of characters. By default, the delimiter is a comma. ColdFusion has many functions for working with lists.

<cfloop> can be used to iterate through a list, performing some function with each list item.

<cfset numlist="1,2,3,4,5"> <cfloop index="num" list="#numlist#"> <cfoutput>#num#</cfoutput> </cfloop> <cfset beatles="paul john ringo george"> <ul> <cfloop index="beatle" list="#beatles#" delimiters=" "> <li><cfoutput>#beatle#</cfoutput></li> </cfloop> </ul>

The first loop will output 1 2 3 4 5. The second loop will output an unordered list of the Beatles' names.

Other Types of Loops

<cfloop> can also be used to loop through other types of data, including queries and structures. Some of these advanced uses of <cfloop> will be covered later in the course.

<cfbreak>

The <cfbreak> tag is used to break out of loops before they have finished processing. The syntax is shown below:

<cfloop index="count" from="1" to="5" step="1"> <cfoutput>#count#</cfoutput> <cfif count GTE 3> <cfbreak> </cfif> </cfloop>

<cfsavecontent>

ColdFusion's <cfsavecontent> tag provides another mechanism for storing data in a variable. The syntax is as follows:

<cfsavecontent variable="var_name"> Variable value </cfsavecontent>

The great thing about <cfsavecontent> is that it can contain other ColdFusion code such as conditionals and loops. This makes it easy to save the results of complicated code as a string of text without appending to a variable with <cfset>.

The following example illustrates the value of <cfsavecontent>.

Code Sample:

FlowControl/Demos/cfsavecontent.cfm
<html>
<head>
<title>cfsavecontent Demo</title>
</head>
<cfset beatles = ArrayNew(1)>
<cfset ArrayAppend(beatles,"John")>
<cfset ArrayAppend(beatles,"Paul")>
<cfset ArrayAppend(beatles,"George")>
<cfset ArrayAppend(beatles,"Ringo")>

<body>
<h1>Store Beatles as HTML List</h1>

<h2>With cfset</h2>
<cfset BeatlesList = "<ul>">
<cfloop from="1" to="#ArrayLen(beatles)#" index="i">
	<cfset BeatlesList = BeatlesList & "<li>" & beatles[i] & "</li>">
</cfloop>
<cfset BeatlesList = BeatlesList & "</ul>">

<cfoutput>#BeatlesList#</cfoutput>

<h2>With cfsavecontent</h2>
<cfsavecontent variable="BeatlesList2">
<ul>
	<cfloop from="1" to="#ArrayLen(beatles)#" index="i">
		<cfoutput><li>#beatles[i]#</li></cfoutput>
	</cfloop>
</ul>
</cfsavecontent>

<cfoutput>#BeatlesList2#</cfoutput>
</body>
</html>

As you can see from the bolded sections, setting and changing values with <cfset> can become ugly and <cfsavecontent> provides a much more elegant solution.