ColdFusion in Context: Augmentation of Automatic Javascript

When you use cfinput and cfselect tags, ColdFusion automatically generates javascript to validate your inputs on the client's browser based on specifications you provide. However, suppose some of your fields need validation formats not supplied by ColdFusion. Can you mix ColdFusion's javascript with your own, or do you have to start from scratch?

It turns out that ColdFusion's javascript routines and validation framework do "play well with others". Here are some brief examples to show how to augment ColdFusion's javascript routines with your own javascript routines for powerful client-side input validation. Along the way, we'll review ColdFusion's javascript framework and see how to roll your own submit-level and change-level validation routines.

First, though, a caveat is in order. Traditional versions of ColdFusion (that is, prior to CFMX) work as shown in this demonstration. CFMX, however, includes /CFIDE/scripts/cfform.js and /CFIDE/scripts/masks.js whenever the CFFORM tag is used. This has three implications:

  1. The size of the javascript is constant: invoking CFFORM sends all the code in these javascript files to the browser regardless of whether it's needed.
  2. When you view source, you won't see the code of the individual routines that ColdFusion uses to do the actual validation. You'll only see the routine names and the environment that invokes them.
  3. A shared hosting environment may not let the browser reach /CFIDE at all. If you run this demo and find that CFFORM never validates your entries, you'll need to copy these scripts from a site that actually provides them, and you'll have to put them where your browser can get them.

Review ColdFusion's Javascript Framework

When ColdFusion sees a cfform tag, it turns it into a normal HTML form tag with an onSubmit call to a javascript routine called _CF_checkmyform(). (Replace "myform" with the name of your form.) When it sees validation requests in cfinput tags, it loads javascript routines needed to perform this validation. Then it automatically modifies the check routine so it calls the appropriate routines for the appropriate fields. If a routine fails, the check routine calls _CF_onError() to display an alert box telling the user about the problem that was encountered.

To see this functionality, create a page that requires the user to enter a number within a specific range. Precede the form itself with an action to display the result. For this example, call the page valmore.cfm.

<cfif isDefined("form.doer") and len(form.doer)>
  <cfoutput>#form.myfield#</cfoutput>
</cfif>
<cfform name="demo" action="valmore.cfm" method="post">
Enter an number from 5 to 7: 
<cfinput type="text" name="myfield" required="yes" range="5,7" 
message="This entry isn't a number from 5 to 7; please try again.">
<input type="submit" name="doer" value="Click Me">
</cfform>

Try this form to assure yourself that it works; then, right click and "view source" to see what the client browser sees. Many javascript routines will come into view. However, the most important ones to your understanding of this framework are the check function and the onError function. Omitting the other routines and removing some white space for brevity, here is what you'll see.

First, you'll see the opening script tag and the onError routine. The script tag identifies this as javascript. The HTML comment hides the actual script from old browsers. The function receives the name of the form, the name of the field, the value in the field, and the error message to be displayed. If invoked, this function displays an error message and returns a value of false to the javascript that called it.

<script LANGUAGE=JAVASCRIPT TYPE="text/javascript" >
<!--
function _CF_onError(form_object, input_object, object_value, 
error_message)
    {
	alert(error_message);
       	return false;	
    }

Just as ColdFusion includes the javascript needed to validate inputs for the forms on a page, so it creates a custom check function for each form on the page to see that the requested validation is performed. Skipping over the actual validation routines (and omitting the _CF_ prefix from this discussion for clarity), here's what the check function for this form does. The name of the form is "demo", so the name of the check function ends in "demo". The hasValue function is true if something has been entered in a field ("myfield" in this case). If it's not true, the check function calls the onError function to display an error message. Otherwise, it calls checkrange to confirm if the value is in the desired range. The checkrange function calls another function; see your browser for this level of detail. If checkrange or a function it calls isn't satisfied, onError is called to display the error message. However, if the input is OK, the check function permits the form to be submitted. Because this is the last function in the group of javascript generated by ColdFusion, it's followed by a script comment to hide the closing HTML tag and then by the closing script tag itself.

function  _CF_checkdemo(_CF_this)
    {
    if  (!_CF_hasValue(_CF_this.myfield, "TEXT" )) 
        {
        if  (!_CF_onError(_CF_this, _CF_this.myfield, 
_CF_this.myfield.value, "This entry isn't a number from 5 to 7; 
please try again."))
            {
            return false; 
            }
        }
 if  (!_CF_checkrange(_CF_this.myfield.value, 5, 7)) 
        {
        if  (!_CF_onError(_CF_this, _CF_this.myfield, 
_CF_this.myfield.value, "This entry isn't a number from 5 to 7; 
please try again."))
            {
            return false; 
            }
        }
    return true;
    }
//-->
</script>

Mix Automatic Javascript with Yours at the Submit Level

*IF* a validation function is already loaded by ColdFusion, you can call it with one of your own. Let's extend the original script as shown and call it valmore2.cfm. It begins with a routine that fails if the input is not less than eight positions or is not a number. This is a hybrid piece of javascript; because, it only does half the work itself and relies on the checknumber function loaded by ColdFusion for the rest. ColdFusion loaded the checknumber routine to validate a different field, and we're just borrowing it for our own purposes.

<script LANGUAGE=JAVASCRIPT TYPE="text/javascript" >
<!--
function lt8(form_object, input_object, object_value, 
error_message)
{
//OK if string Less Than 8 characters
if (object_value.length < 8)
  return _CF_checknumber(object_value);
else
  return false;
}
//-->
</script>

The rest of the code is slightly different this time. The first field is now optional, and ColdFusion's default error message will be displayed if it has a problem. The second field is a required number from 8 to 10 which must be less than 8 characters in length and has a custom error message.

<cfif isDefined("form.doer") and len(form.doer)>
  <cfoutput>#form.myfield# #form.myfield2#</cfoutput>
</cfif>
<cfform name="demo" action="valmore2.cfm" method="post"><br>
Enter a number from 5 to 7: <cfinput type="text" name="myfield" 
validate="float" range="5,7"><br>
Enter a short number from 8 to 10: <cfinput type="text" 
name="myfield2" onvalidate="lt8" required="yes" range="8,10" 
message="2nd value must be in range and less than 8 chars">
<input type="submit" name="doer" value="Click Me">
</cfform>

When you right-click on a browser featuring this page, you'll see that ColdFusion has automatically integrated the tests you're letting ColdFusion handle (by specifying with required, range, and validate attributes) and the tests you're handling yourself (through the onvalidate attribute) into the overall check routine for the form. It would handle them together even if this example didn't borrow code to support the routine called with onvalidate. The entire form is automatically checked when the user tries to submit it.

Supplement Submit-Level Checks with Change-Level Javascript

Suppose you want to borrow a routine that isn't needed for any other field on the form. You can't use the obvious method of combining a validate and onvalidate attribute in the same field; it won't work. You could manually copy ColdFusion's javascript into the page. However, there's another method which has the added benefit of providing immediate feedback at the field level. You can use ColdFusion's validate, range, and required attributes (which will be tested when the form is submitted) in conjunction with a javascript test to be called by "onChange" for immediate feedback when the field changes. This code provides an example: call it valmore3.cfm.

It begins with a javascript routine, slightly changed from before. This time, it doesn't include tests by ColdFusion; because, we'll make them separately when the user tries to submit the form. When a failure occurs, it displays a custom error message and empties the field. The reason it empties the field is that there's nothing to stop the user from pressing submit after receiving this message, and failure to empty the field before returning control to the user would let the user submit an invalid input.

<script LANGUAGE=JAVASCRIPT TYPE="text/javascript" >
<!--
function lt8(fieldname)
{
//OK if string less than 8 characters
if (fieldname.value.length < 8)
  return true;
else
  alert("this field cannot exceed 7 characters");
  fieldname.value="";
  return false;	
}
//-->
</script>

The remaining code differs from the very first example in that it uses the validate attribute in the same field as an onChange call to javascript. Note that the onChange call has a parameter. It's not just a field attribute like validate and onvalidate but a true javascript call; the parameter "this" lets lt8 work with this object (this field).

<cfif isDefined("form.doer") and len(form.doer)>
  <cfoutput>#form.myfield#</cfoutput>
</cfif>
<cfform name="demo" action="valmore3.cfm" method="post">
short float:<cfinput type="text" name="myfield" 
validate="float" required="yes" onChange="lt8(this)">
<input type="submit" name="doer" value="Click Me">
</cfform>

Implications for Development

First, ColdFusion validates inputs at the form level when the user tries to submit the entire form. ColdFusion's default error messages indicate which field has the problem. If you provide a custom error message, make it clear which field is in error. For any given field, you can use your own tests or use tests from ColdFusion's repetoire. (We even saw a rare example that borrowed from javascript already loaded by ColdFusion to support a non-ColdFusion test, but this was done just to prove a point and isn't something you'll probably do very often.) Whether you write the test, or ColdFusion writes the test, ColdFusion will integrate all these routines into a consoldated check that fires when the user tries to submit the form.

Second, onChange validates changes at the field level. If you have unique formatting requirements or have a very large form, consider augmenting submit-level checks with change-level checks to reduce user frustration. Note that using onChange, your error message doesn't need to name a specific field. Because the message will appear as the user tries to leave the field, it will be obvious which field is in error. Bear in mind that onChange won't fire if no change is made and in any case won't block form submission directly. Therefore, if you catch an error with onChange, force the field value to something good or to something that a form-level check can block.

Third, it should be clear by now, especially if you've performed a right-click on each of these examples and viewed the source for each one, that client-side validation can take as much or as little time as you're willing to spend. The issue isn't how much data integrity you want. You should, alas, check your data on the server anyway to guard against a few nasty users. The issue is user convenience.

Fortunately, ColdFusion provides some conveniences to the developer. When you specify a few attributes, you can require a field to be filled in, restrict its numeric range, and check for common formats without writing any javascript of your own. If you need custom routines, you can call them with the "onvalidate" attribute, and ColdFusion will include your routines in automatically generated submit-level, client-side data validation. When nothing else will do, it lets you easily augment this overall submit-level framework with your own change-level routines. =Marty=tcard, date, eurodate, float, integer, range, social_security_number, telephone, time, and zipcode. It confirms that the entry, if present, is a number. The number may begin with a sign (+/-) and may contain one decimal point. However, a number in scientific notation will be rejected.

checkphone: 1116; used to validate telephone. It confirms that the entry, if present, follows a typical format for a United States telephone number (including area code): nnn nnn-nnnn. The separators may be hyphens or spaces.

checkrange: 382; used to validate date, eurodate, range, telephone, and time. This is a front end for numberrange. If an entry is present and is a number, it invokes numberrange and returns true if numberrange is satisfied.

checkssc: 848; used to validate social_security_number. It confirms that the entry, if present, follows the usual format for a United States Social Security Number: nnn-nn-nnnn. The separators may be spaces, hyphens, plus signs, or periods.

checktime: 1148; used to validate time. It confirms that the entry, if present, is in the format hh:mm:ss (or hh:mm) where hh may be from 00 to 23, mm from 00 to 59, and ss from 00 to 59. The routine has a minor flaw: it accepts times like "10:", "10::", and "10:09:". It also contains a misleading comment which implies it cares about dates. (It doesn't; it only accepts times.)

checkzip: 784; used to validate zipcode. It confirms that the entry, if present, is in one of two formats for United States postal "zip" codes: nnnnn or nnnnn-nnnn. The separator (for the long format) may be a hyphen or a space.

hasValue: 654; used to require that a field is filled in or a selection is made in a checkbox or select list. This is multi-purpose routine that covers all three types of input controls.

numberrange: 324: used to validate date, eurodate, range, telephone, and time. If an entry is present, it confirms that the entry is neither smaller nor larger than the specified range.

Implications for Development

First, don't be concerned that the CFFORM will bloat your pages. Only a small amount of javascript is generated by default; the rest is only generated if you need it.

Second, don't bother writing new routines for the validation functions already handled by ColdFusion (except perhaps the time function). The best code is code you don't have to write.

Third, if you do have a validation need that these routines won't fill, use them as a starting point and tell others what you've learned.

ColdFusion generates a framework and many of the javascript routines you need in order to handle client-side data validation. Because the javascript is not part of the ColdFusion code, you don't have to add, modify, or review it as you add, modify, and review the rest of your code. You usually won't even see it. Automatically generated javascript is just another feature that makes ColdFusion the choice of champions. =Marty=
[Experience with CFMX, a version not available when this demonstration was originally published, led to the caveat in the introduction.]