facebook google plus twitter
Webucator's Free ColdFusion MX Tutorial

Lesson: Form Validation

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

Lesson Goals

  • To understand the basics of the <cfform> tag.
  • To work with ColdFusion's auto-generated client-side form validation using <cfform>.
  • To work with ColdFusion's auto-generated server-side form validation using <cfform>.
  • To work with ColdFusion's auto-generated server-side form validation without <cfform>.
  • To create input masks.
  • To write your own server-side form validation code.

<cfform> Basics

The <cfform> tag is used to generate HTML forms. ColdFusion form controls, such as <cfinput>, <cfselect>, and <cftextarea> are used inside of <cfform> to create HTML entry fields. The table below shows <cfform>'s attributes for HTML forms.

Major <cfform> Attributes for HTML Forms
Attribute Description
name form name
action form action
method form method: "post" or "get"
format "html" for HTML forms

Code Sample:

FormValidation/Demos/AddEntry-cfform.cfm
<html>
<head>
  <title>Running Log</title>
</head>
<body>

<cfform name="RunningForm" method="post" action="AddEntry-cfform.cfm" format="html">
<table>
<tr>
	<td>Date:</td>
	<td><cfinput type="text" name="date" size="20"></td>
</tr>
<tr>
	<td>Distance:</td>
	<td><cfinput type="text" name="distance" size="20"></td>
</tr>
<tr>
	<td>Time:</td>
	<td><cfinput type="text" name="time" size="20"></td>
</tr>
<tr>
	<td>Comments:</td>
	<td><cftextarea rows="3" cols="40" name="comments"></cftextarea></td>
</tr>
<tr>
	<td colspan="2" align="right">
		<cfinput type="submit" name="Add Entry" value="Add Entry">
	</td>
</tr>
</table>
</cfform>

</body>
</html>

The generated HTML form code is shown below:

<form name="RunningForm" action="AddEntry-cfform.cfm" method="post" onsubmit="return _CF_checkRunningForm(this)"> <table> <tr> <td>Date:</td> <td><input name="date" id="date" type="text" size="20"/></td> </tr> <tr> <td>Distance:</td> <td><input name="distance" id="distance" type="text" size="20"/></td> </tr> <tr> <td>Time:</td> <td><input name="time" id="time" type="text" size="20"/></td> </tr> <tr> <td>Comments:</td> <td><textarea name="comments" rows="3" cols="40" ></textarea></td> </tr> <tr> <td colspan="2" align="right"> <input name="Add Entry" id="Add Entry" type="submit"/> </td> </tr> </table> </form>

As you can see, it is a basic HTML form with nothing too special about it. However, ColdFusion form tags can do some pretty neat things with the right attributes. In this lesson, we'll learn how to use them to validate forms.

Auto-generated Form Validation

Form Validation with <cfform>

Using the <cfform> in place of the <form> tag allows you to take advantage of ColdFusion's auto-generated validation code, which can be run client-side with JavaScript before the form actually submits or server-side after the form submits.

For input fields that you wish to validate in this way, use <cfinput> instead of <input>. To require that a field be filled in, set required="yes" in the <cfinput> tag.

In addition to checking whether or not a field is filled in, you can check that fields conform to specific types of patterns by assigning the appropriate value to the validate attribute of <cfinput> or <cftextarea>.

<cfinput> and <cftextarea> Validate Values
Suffix Description
numeric Numeric. The following special characters are allowed and kept: $ ¤ ¥ £ +.
integer Integer. The following special characters are allowed but stripped: $ ¤ ¥ £ +.
float Float. The following special characters are allowed but stripped: $ ¤ ¥ £ +.
range Value must be within range specified by the value attribute in the format "min=minvalue max=maxvalue". No custom error message is allowed.
date Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC date format (without the time).
time Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC time format (without the date).
usdate U.S. date. Allowed formats: m/d/y, m-d-y , or m.d.y
eurodate European date. Allowed formats: d/m/y, d-m-y, or d.m.y
creditcard Number between 13-16 digits that conforms to the mod10 algorithm. Spaces and dashes are stripped.
SSN Nine-digit Social Security number separated by dashes or spaces.
telephone U.S. telephone number with or without area codes and extensions.
zipcode 5 or 9 digit U.S. ZIP code.
email Valid email address.
URL Valid URL.
boolean Boolean. (Yes, No, True, False, 1, 0)
UUID Universally unique identifier (UUID) that follows the ColdFusion format, xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx, where x is a hexadecimal number.
GUID Unique identifier that follows the Microsoft/DCE format, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is a hexadecimal number.
noblanks Value must contain at least one non-whitespace character. Field must also be specified as required using required.
maxlength Value cannot contain more characters than specified by the maxlength attribute. No custom error message is allowed.
regex Value must match regular expression defined in the pattern attributed.
SubmitOnce Used in submit buttons to prevent a user from submitting a form multiple times by mistake.

The validateat Attribute

The validateat attribute specifies where the form validation should take place. Options are onsubmit (default), onblur, and onserver. The first two options use client-side form validation. The last one uses server-side validation.

Below is an example of how to use <cfform> to validate the running log entry form using client-side form validation.

Code Sample:

FormValidation/Demos/AddEntry-onsubmit.cfm
---- C O D E   O M I T T E D ----
<cfform name="RunningForm" method="post" action="#CGI.SCRIPT_NAME#" format="html">
<table>
<tr>
	<td>Date:</td>
	<td><cfinput type="text" name="date" size="20" required="yes" validate="date" validateat="onsubmit" message="You must enter a valid date."></td>
</tr>
<tr>
	<td>Distance:</td>
	<td><cfinput type="text" name="distance" size="20" required="yes" validate="float" validateat="onsubmit" message="You must enter a valid number for the distance."></td>
</tr>
<tr>
	<td>Time:</td>
	<td><cfinput type="text" name="time" size="20" required="yes" validate="regex" pattern="^\d{1,3}:\d{0,2}$" validateat="onsubmit" message="You must enter a valid time in the format mm:ss."></td>
</tr>
<tr>
	<td>Comments:</td>
	<td><cftextarea rows="3" cols="40" name="comments" required="yes" validate="maxlength" maxlength="50" validateat="onsubmit" message="The comment must be between 1 and 50 characters."></cftextarea></td>
</tr>
<tr>
	<td colspan="2" align="right">
		<cfinput type="submit" name="AddEntry" value="Add Entry">
	</td>
</tr>
</table>
</cfform>

</body>
</html>

The form checks for the following:

  1. The date is filled in and is a valid date.
  2. The distance is filled in and is a valid float.
  3. The time is filled in and corresponds to a specified regular expression. (Note this is a time in minutes and seconds (mm:ss), not a time of day, so we don't check to see if it is a valid time.)
  4. The comments field is filled in.

If the form is submitted with errors, the following alert will pop up:

Below is an example of how to use <cfform> to validate the running log entry form using server-side form validation. The only change is the value of the validateat attribute.

Code Sample:

FormValidation/Demos/AddEntry-onserver.cfm
---- C O D E   O M I T T E D ----
<cfform name="RunningForm" method="post" action="#CGI.SCRIPT_NAME#" format="html">
<table>
<tr>
	<td>Date:</td>
	<td><cfinput type="text" name="date" size="20" required="yes" validate="date" validateat="onserver" message="You must enter a valid date."></td>
</tr>
<tr>
	<td>Distance:</td>
	<td><cfinput type="text" name="distance" size="20" required="yes" validate="float" validateat="onserver" message="You must enter a valid number for the distance."></td>
</tr>
<tr>
	<td>Time:</td>
	<td><cfinput type="text" name="time" size="20" required="yes" validate="regex" pattern="^\d{1,3}:\d{0,2}$" validateat="onserver" message="You must enter a valid time in the format mm:ss."></td>
</tr>
<tr>
	<td>Comments:</td>
	<td><cftextarea rows="3" cols="40" name="comments" required="yes" validate="maxlength" maxlength="50" validateat="onserver" message="The comment must be between 1 and 50 characters."></cftextarea></td>
</tr>
<tr>
	<td colspan="2" align="right">
		<cfinput type="submit" name="AddEntry" value="Add Entry">
	</td>
</tr>
</table>
</cfform>

</body>
</html>

The form makes the same checks as we saw with the client-side validation, but is validated server-side.

If the form is submitted with errors, the page will display as follows:

Although it is possible to customize this error template, you do not have as much control over the look and feel of the page as you would if you wrote the form validation code yourself.

Behind the Curtain of Server-Side Validation

What's happening behind the curtain here? The <cfform> tag and its friends (<cfinput>, <cfselect>, etc.) are performing a little magic by auto-generating standard HTML form controls. For each piece of validation, hidden fields are added that ColdFusion later uses to know what type of validation to perform. The developer is also able to add these hidden fields directly, which is useful when not using <cfform>. In older versions of ColdFusion (MX 6 and earlier), this was the only way to get ColdFusion to auto-generate server-side validation. This is discussed in the next section.

Server-Side Validation without <cfform>

Through the use of hidden <input> fields in a form, you can get ColdFusion to do some basic server-side form validation for you. The name of the hidden fields must be the same name as the field to be validated followed by an underscore (_) followed by a suffix, which indicates the type of validation. Starting with ColdFusion MX 7, suffixes begin with "cfform"; however, for the validation types that were supported in older versions, the old suffix will still work.

ColdFusion returns the value of the hidden field as the error message.

The table below describes the hidden fields suffixes.

Hidden Field Form Validation Suffixes
Suffix Old Suffix Description
_cfformrequired _required Field must be filled out or selected. If this is not set, then the field will not be required, even if other validation rules are set.
_cfformnumeric n/a Numeric. The following special characters are allowed: $ ¤ ¥ £ + and kept.
_cfforminteger _integer Integer. The following special characters are allowed: $ ¤ ¥ £ + but stripped.
_cfformfloat _float Float. The following special characters are allowed: $ ¤ ¥ £ + but stripped.
_cfformrange _range Value must be within range specified by the value attribute in the format "min=minvalue max=maxvalue". No custom error message is allowed.
_cfformdate _date Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC date format (without the time).
_cfformtime _time Date/time that ColdFusion can understand. ColdFusion converts the value into ODBC time format (without the date).
_cfformusdate n/a U.S. date. Allowed formats: m/d/y, m-d-y , or m.d.y
_cfformeurodate _eurodate European date. Allowed formats: d/m/y, d-m-y, or d.m.y
_cfformcreditcard n/a Number between 13-16 digits that conforms to the mod10 algorithm. Spaces and dashes are stripped.
_cfformSSN n/a Nine-digit Social Security number separated by dashes or spaces.
_cfformtelephone n/a U.S. telephone number with or without area codes and extensions.
_cfformzipcode n/a 5 or 9 digit U.S. ZIP code.
_cfformemail n/a Valid email address.
_cfformURL n/a Valid URL.
_cfformboolean n/a Boolean. (Yes, No, True, False, 1, 0)
_cfformUUID n/a Universally unique identifier (UUID) that follows the ColdFusion format, xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx, where x is a hexadecimal number.
_cfformGUID n/a Unique identifier that follows the Microsoft/DCE format, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where x is a hexadecimal number.
_cfformnoblanks n/a Value must contain at least one non-whitespace character. Field must also be specified as required using _cfformrequired.
_cfformmaxlength n/a Value cannot contain more characters than specified by the value attribute. No custom error message is allowed.
_cfformregex n/a Value must match regular expression.
<input type="text" name="age"> <input type="hidden" name="age_cfforminteger" value="Age must be a valid integer.">

Below is an example of how to use hidden fields to validate the running log entry form.

Code Sample:

FormValidation/Demos/AddEntry-hidden.cfm
---- C O D E   O M I T T E D ----
<form method="post" action="#CGI.SCRIPT_NAME#">
</cfoutput>
<input type="hidden" name="submitted" value="true">
<table>
<tr>
	<td>Date:</td>
	<td><input type="text" name="date" size="20"></td>
	<input type="hidden" name="date_cfformrequired" value="You must enter a date.">
	<input type="hidden" name="date_cfformdate" value="The date you entered is not a valid date.">
</tr>
<tr>
	<td>Distance:</td>
	<td><input type="text" name="distance" size="20"></td>
	<input type="hidden" name="distance_cfformrequired" value="You must enter a distance.">
	<input type="hidden" name="distance_cfformfloat" value="The distance you entered is not a valid number.">
</tr>
<tr>
	<td>Time:</td>
	<td><input type="text" name="time" size="20"></td>
	<input type="hidden" name="time_cfformrequired" value="You must enter a time.">
	<input type="hidden" name="time_cfformregex" value="^\d{1,3}:\d{0,2}$">
</tr>
<tr>
	<td>Comments:</td>
	<td><input type="text" name="comments" size="50"></td>
	<input type="hidden" name="comments_cfformrequired" value="You must enter a comment.">
	<input type="hidden" name="comments_cfformmaxlength" value="50">
</tr>
<tr>
	<td colspan="2" align="right">
		<input type="submit" name="Add Entry">
	</td>
</tr>
</table>
</form>

</body>
</html>

The form makes the same checks as before, but using this method, it is possible to have different error messages based on the type of error.

If the form is submitted without filling any of the fields in, a page that looks like the screenshot below will appear:

If the form is completely filled in, but the fields are invalid, the resulting page will look like this:

Although it is possible to customize this error template, you do not have as much control over the look and feel of the page as you would if you wrote the form validation code yourself.

Masking Input Values

The <cfinput> tag has a mask attribute that controls the format the user enters into a text field. The syntax is as follows:

<cfinput type="text" name="phone" mask="(999) 999-9999">

The table below shows the mask characters for HTML fields:

Text Input Mask Characters
Character Description
A A-Z (case-insensitive)
X A-Z (case-insensitive) or 0-9
9 0-9
? Any character
Other Characters Inserts literal character

The code sample below shows some samples of masking:

Code Sample:

FormValidation/Demos/Masks.cfm
<html>
<head>
  <title>Masks</title>
</head>
<body>

<cfform method="post" action="#CGI.SCRIPT_NAME#" format="html">
	Phone: <cfinput type="text" name="phone" mask="(999) 999-9999"><br/>
	SSN: <cfinput type="text" name="ssn" mask="999-99-9999"><br/>
	Course ID: <cfinput type="text" name="course_id" mask="AAA99X"><br/>
	<cfinput type="submit" name="Submit" value="Submit">
</cfform>

</body>
</html>

Open the page in your browser and enter fields. You will notice that the format of your entry is controlled and that some characters are automatically inserted.

Custom Server-Side Form Validation

Writing your own custom server-side form validation gives you a lot more flexibility and control. Take a look at the following code.

Code Sample:

FormValidation/Demos/AddEntry-custom.cfm
               <cfparam name="FORM.date" default="">
<cfparam name="FORM.distance" default="">
<cfparam name="FORM.time" default="">
<cfparam name="FORM.comments" default="">

<html>
<head>
  <title>Running Log</title>
  <style>
  	.errors {color:red; font-weight:bold}
	.cool {color:black}
  </style>
</head>
<body>
<cfparam name="errors" default="">
<cfif isDefined("FORM.submitted")>
	<cfif NOT isDate(FORM.date)>
		<cfset errors = errors & "<li>The date is invalid.</li>">
		<cfset dateclass="errors">
	</cfif>
	
	<cfif NOT ListLen(FORM.distance," ") EQ 2>
		<cfset errors = errors & "<li>The distance must be in the format <i>num units</i>.</li>">
		<cfset distanceclass="errors">
	<cfelse>
		<cfset intDistance = ListGetAt(FORM.distance,1," ")>
		<cfset units = ListGetAt(FORM.distance,2," ")>
		<cfif NOT isNumeric(intDistance)>
			<cfset errors = errors & "<li>The distance must be in the format <i>num units</i>.</li>">
			<cfset distanceclass="errors">
		</cfif>
	</cfif>
	
	<cfif Len(errors) EQ 0>
		<cfset RunningLogPath = ExpandPath("Logs/RunningLog.txt")>
		<cfset Tab = chr(9)>
		<cfset outputstring = "#FORM.date##Tab##FORM.distance##Tab##FORM.time##Tab##FORM.comments#">
		
		<cffile action="append" file="#RunningLogPath#" output="#outputstring#" addnewline="yes">
		<h1 align="center">Entry added</h1>
		<a href="RunningLog.cfm">Running Log</a>
		
		<!---Clean up variables--->
		<cfset FORM.date="">
		<cfset FORM.distance="">
		<cfset FORM.time="">
		<cfset FORM.comments="">
	</cfif>
</cfif>

<h1 align="center">Add Entry</h1>
<cfoutput>
<form method="post" action="#CGI.SCRIPT_NAME#">
<input type="hidden" name="submitted" value="true">
<table>
<cfparam name="dateclass" default="cool">
<cfparam name="distanceclass" default="cool">
<cfparam name="timeclass" default="cool">
<cfparam name="commentsclass" default="cool">
<cfif Len(errors) GT 0><!---checking for errors--->
	<tr><td colspan="2" style="margin-left:20px">
		<h3>Errors</h3>
		<ul class="errors">
			#errors#
		</ul>
	</td></tr>
</cfif>
<tr>
	<td>Date:</td>
	<td><input type="text" name="date" size="20" value="#FORM.date#" class="#dateclass#"></td>
</tr>
<tr>
	<td>Distance:</td>
	<td><input type="text" name="distance" size="20" value="#FORM.distance#" class="#distanceclass#"></td>
</tr>
<tr>
	<td>Time:</td>
	<td><input type="text" name="time" size="20" value="#FORM.time#" class="#timeclass#"></td>
</tr>
<tr>
	<td>Comments:</td>
	<td><input type="text" name="comments" size="50" value="#FORM.comments#" class="#commentsclass#"></td>
</tr>
---- C O D E   O M I T T E D ----

This form only validates the first two fields.

  • The date must be a valid date.
  • The distance must be in the format "num units" (e.g, 9 miles).

If the form is submitted without filling any of the fields in, a page that looks like the screenshot below will appear:

The validation is handled as follows:

  1. An errors variable is created using <cfparam> containing an empty string.
  2. When the form is submitted, the fields needing validation are checked one by one. If an error is found, text is appended to the errors variable in the form of an HTML list item and a variable holding the class for that field is set to "errors". That variable is used inside the <input> fields to determine how the field should be displayed.
  3. After each field has been checked, the length of the string held in errors is checked. If it is a zero-length string, that means there are no errors and the entry is added to the log.
  4. Within the HTML form itself, the length of errors is checked again. If it is not a zero-length string, then the error message is output.

IsValid()

The IsValid() function is useful for checking whether a variable or value is of a certain data type or meets pattern, size or length constraints. There are three possible signatures for IsValid().

IsValid(type, value) isValid("range", value, min, max) isValid("regex", value, pattern)

Possible types are the same as those shown in the "<cfinput> and <cftextarea> Validate Values" table at the beginning of this lesson. In addition, the type can be any of the following:

Additional Types for IsValid()
Suffix Description
any Same as IsSimpleValue().
array Same as IsArray().
binary Same as IsBinary().
query Same as IsQuery().
struct Same as IsStruct().

Custom Form Validation

Duration: 20 to 30 minutes.
  1. Open FormValidation/Exercises/AddEntry.cfm in your editor.
  2. Add the following validation.
    • Time format should be "##:##".
    • Comments should be required and not longer than 50 characters.
  3. Open AddEntry.cfm in your browser to test your new page.
  1. The date entered should be in the past. You can use the DateCompare() function to compare it to the current time. If the date has not yet occurred, return an error.
  2. Using your other knowledge of ColdFusion, let the user know how fast she was running.

Solution:

FormValidation/Solutions/AddEntry.cfm
---- C O D E   O M I T T E D ----
<cfif ListLen(FORM.time,":") NEQ 2>
		<cfset errors = "#errors#<li>The time must be in the format <i>MM:SS</i>.</li>">
		<cfset timeclass="errors">
	<cfelse>
		<cfset minutes = ListGetAt(FORM.time,1,":")>
		<cfset seconds = ListGetAt(FORM.time,2,":")>
		<cfif NOT isNumeric(minutes) OR NOT isNumeric(seconds) OR Len(minutes) GT 2 OR Len(seconds) GT 2>
			<cfset errors = "#errors#<li>The time must be in the format <i>MM:SS</i>.</li>">
			<cfset timeclass="errors">
		</cfif>
	</cfif>
	
	<cfif Len(Trim(FORM.comments)) EQ 0 OR Len(Trim(FORM.comments)) GT 50>
		<cfset errors = "#errors#<li>You must include a comment of up to 50 characters.</li>">
		<cfset commentsclass="errors">
	</cfif>
---- C O D E   O M I T T E D ----

Challenge Solution:

FormValidation/Solutions/AddEntry-challenge.cfm
---- C O D E   O M I T T E D ----
<cfif isDefined("FORM.submitted")>
	<cfif NOT isDate(FORM.date)>
		<cfset errors = errors & "<li>The date is invalid.</li>">
		<cfset dateclass="errors">
	<cfelseif DateCompare(Now(),FORM.date) EQ -1>
		<cfset errors = errors & "<li>The date must be in the past.</li>">
		<cfset dateclass="errors">
	</cfif>
---- C O D E   O M I T T E D ----
		<!---Calculate Speed--->
		<cfset totalhours = (minutes + (seconds/60))/60>
		<cfset speed = intDistance/totalhours>
		<cfoutput>
		<br>You were running at an average speed of #speed# #units# per hour.
		</cfoutput>
---- C O D E   O M I T T E D ----