Welcome!

ColdFusion Authors: Yakov Fain, Maureen O'Gara, Nancy Y. Nee, Tad Anderson, Daniel Kaar

Related Topics: ColdFusion

ColdFusion: Article

JavaScript Without the Headaches

JavaScript Without the Headaches

Coding ColdFusion and coding JavaScript are about as far apart on the productivity spectrum as it's possible to be. CF tags are neat, easy to read, tolerant, and fun to write. JavaScript is none of that. It has all the drawbacks of traditional languages, with the added disadvantage of sitting somewhat awkwardly within the HTML document model.

Anything but the most trivial JavaScript is difficult to write, difficult to debug, and difficult for either the author or another developer to revisit, but, as much as I'd like to, it isn't possible to do without it for interactive Web site development. For all its faults, JavaScript remains the principal means of delivering client-side functionality.

For better or worse, we have to learn to live with it, and fortunately ColdFusion makes it easy to integrate JavaScript into your apps without having to code and debug chunks of JavaScript every time you want to extend the basic HTML form controls.

Making the Most of CFFORM and CFINPUT
The primary and most underrated way of integrating JavaScript with your CF apps is through simple CFFORM and CFINPUT tags. It's wrong to assume that just because you want to do advanced validation you need to start from scratch coding all your own routines. These tags form a great platform to start building more advanced validations.

Obviously, there are the REQUIRED and VALIDATE attributes that will do the most common validations without having to write or debug JavaScript, but the ONVALIDATE and ONERROR attributes can be used to do more advanced validations without necessarily having to get too involved either.

About OnValidate
ONVALIDATE is the more useful and more powerful. The value supplied to the attribute is simply the name of the JavaScript function to call to validate the field. The parameters passed to the routine are always the same: the form object, the field object, and the value of the field. For example, in a basic validation where you want a password field to be 6 letters or more, you'd use the following code:

This would then be called by a CFINPUT tag like this:

message="Please enter a password of at least than 6 letters"
onvalidate="validatePassword">

(See Listing 1 for complete listing. This also shows a neat trick for using a hidden cfinput to validate a textarea tag.)

On balance, the fact that the parameters are fixed like this is a help, although the drawbacks are obvious. What if you want to vary the required length? You can't pass the length as a parameter, so you end up altering your JavaScript - which is specifically what we want to avoid by using tags like CFINPUT.

This problem (which we'll look at later) is outweighed by the convenience of not having to code references to the form, field, and value each time. It doesn't, of course, limit you to only referencing that field - you can reference other fields quite simply, as in this check for a confirmation password field:

// Check that the confirm password field matches the first
function checkPasswordsMatch(form,field,value) {
if (value != form.pw.value) {
return 0;
}
else {
return 1;
}
}

Which you'd use in conjunction with a CFINPUT:

Confirm: onvalidate="checkPasswordsMatch">

(See Listing 2 for complete example.)

This again suffers from the problem that the validation script isn't parameterized - the name of the first password field is hardwired into the code, something that'd give the object purists palpitations.

About OnError
ONERROR can be used to override the default behavior of simply displaying an error message if the validation fails. You can use it to display different error messages for different validation failures (e.g., blank or not enough characters).

For instance, the following script could be used to validate the pw_check field shown previously in Listing 2.

function pw_check_error(form,field,value) {
if (value.length == 0) {
alert('Please confirm your password');
}
else if (value != form.pw.value) {
alert('The passwords do not match');
}


}

(See Listing 3 for complete example.)

NB the onerror script is called if required=yes and the field is blank, as well as when the onvalidate function returns false.

The Challenges of Generated Code
This now has made the problem of having parameters hardwired into the JavaScript even worse. The error messages should be parameterized or even dynamic if we're using a multilanguage setup.

Experienced developers looking at this will be thinking it might be best to start from scratch with their own validation routines, and when you look at code like Listing 3, you can see why.

The solution most developers would adopt is to write JavaScript routines that take things like the minimum length and the error messages as parameters and apply them either in onchange handlers for the fields or in onsubmit handler for the form. Something like this:

<input onchange="CheckPW(this.form, this,this.value, 6,
'Please confirm your password', 'The passwords do not match')">

(Note: Don't try this! It's just hypothetical.)

This is the first step on the familiar path of teeth-gnashing frustration that is coding your own JavaScript, and before we go down that road, let's look at another option for tidying up our code.

If you look at the code generated by CFFORM, you'll notice one thing very quickly - there's lots of it. Also you'll see that it isn't especially generic. The names of fields are hardwired into the code in much the same way as the examples here.

The same is true if you look at the JavaScript generated by several other automated systems. The developers of these systems have made a conscious decision to do it this way instead of trying to create completely reusable functions and objects. JavaScript just doesn't warrant effort spent on trying to make it neat and generic. You can waste hours trying to get it right and then find it doesn't work on one particular build of Mozilla.

Solving the Problem with Custom Tags
Instead, there's a different philosophy you can adopt - that it doesn't necessarily matter how neat the JavaScript is or how much of it there is, as long as it's generated automatically and the code that generates it is properly modular and reusable.

Say for instance we wanted to tidy up our password field. According to correct design principles, the only things we should be including in the page are the necessary details of the field - its name, its minimum length, the fact that it's a password, and that we want a "check" field, all of which can be neatly done with a ColdFusion custom tag:

(See Listing 4; also see the listing for cf_password.)

All the JavaScript and the cf input tags can then be generated by the custom tag, with the JavaScript placed correctly in the element with a CFHTMLHEAD. The fieldname "pw" may be hardwired into the JavaScript, but as long as it isn't hardwired into the CFML that generates the JavaScript, that doesn't matter. The same goes for the minimum length. This system is all correctly parameterized and reusable without having to waste hours coding JavaScript. It's also readable and easily understandable in the same way as CFML form tags.

Whenever you include client-side functionality you should always adopt this approach, either writing your own custom tags - or even better - using some of the many excellent tags in the developer's exchange. One of the most common requirements is for "related selects" - where selecting a value in one select list changes another list to show only related values.

If you aren't already, you should be using one of the many custom tags available to do this job. Cf_twoselectsrelated (see Figure 1) is a great, simple tag that can be customized quite easily to fit in with cfform rather than using its own validations. The hidden cfinput technique allows for easy validation. In Listing 5, the name of the second select is SUB_CATEGORY, and we can require this or validate it using:

 

Rather than modifying the tag itself to include this, I recommend creating a new tag that then calls Cf_twoselectsrelated; otherwise you can end up with versioning problems. (Of course, another option for providing these kinds of interfaces is Flash and Flash Remoting. For more information, see Ben Forta's November 2002 CFDJ article, "Data Entry Reformed," Vol. 4, issue 11. But there are clearly times when JavaScript alone may make more sense.)

Other useful custom tags include cf_swapbox (Figure 2, Listing 6), cf_cross_select, tags for picking dates or times, and tags for dividing forms up into "tabbed" controls.

 

If you look at the JavaScript generated by any of these tags, you'll find the same story - lots of JavaScript with hardwired field names and parameter values. It doesn't matter, as long as the ColdFusion generating it is properly modular. If you're going to start coding your own custom tags, you can spare yourself many hours of frustrating debugging by following the same philosophy.

And while this article presumes you're already skilled with JavaScript, if you're just getting started and could use some hand-holding, see Charlie Arehart's June 2000 CFDJ article, "Getting Focus(ed) - and a Quick JavaScript Overview," Vol. 2, issue 6.

Spend time on what's most productive: keep your ColdFusion pages neat, and don't lose any sleep over your JavaScript.

More Stories By Tom Peer

Tom Peer has been in electronic publishing of one sort or another for ten years, including a stint as manager of New Scientist Online (www.newscientist.com). He specializes in taking printed publications online and has recently completed the online edition of The World Handbook of Stock Exchanges (www.exchange-handbook.com).

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.