Validation in cf_sebForm
Although I briefly touched on validation in cf_sebForm, it is certainly a topic that deserves more attention. This is especially true in light of Ben Nadel's great entry on data type validation versus data value validation (and his final thoughts on the same topic). It is an important distinction and I want to cover how cf_sebForm deals with the challenge.
The most basic type of validation offered in cf_sebForm is simply checking for required fields. To do this, simply add a required attribute to the field:
This will add both a client-side check with JavaScript as well as server-side validation.
You can also use the "type" attribute, to define a data type for the field. For example, you could define a field as an integer:
Or as an email address:
These are both validation types. Each validation type is defined by a regular expression. As with the required check, the regular expression is evaluated by JavaScript on the client and by ColdFusion on the server.
As of the first beta, cf_sebForm has four built-in validation types:
- GUID
- integer
- zipcode
If you can think of other validation types that should be built-in, let me know and I can investigate adding them in to the next build.
You can also add your own custom validation types to cf_sebForm. To add validations to cf_sebForm, pass a structure of validations to the "validations" attribute of cf_sebForm. For example:
<cfset sValidations["YahooMail"] = "^['_a-z0-9-]+(\.['_a-z0-9-]+)*@yahoo.com">
<cf_sebForm validations="#sValidations#">
<cf_sebField type="text" name="fName" label="First Name" required="true">
<cf_sebField type="text" name="lName" label="Last Name">
<cf_sebField type="YahooMail" name="email" label="Yahoo! Email Address">
<cf_sebField type="submit" label="Save">
</cf_sebForm>
See it in action (and feel free to disable JavaScript on your browser)
If your custom validation is one that you will use across a whole site, you may not want to pass it in to each instance of cf_sebForm on your site. Fortunately, you can use global custom tag attributes with cf_sebForm to avoid that tedium.
Of course, checking for a Yahoo! Email address is exactly the type of validation that Ben mentioned as a "data value" validation, which should be processed in a CFC. So, how does cf_sebForm solve this problem, but keep the developer code easy and the user happy?
Here is how I might write the method in the CFC:
<cfargument name="fName" type="string" required="no">
<cfargument name="lName" type="string" required="no">
<cfargument name="email" type="string" required="no">
<cfif StructKeyExists(arguments,"email") AND Len(arguments.email)>
<cfif NOT (
isEmail(arguments.email)
AND ListLast(arguments.email,"@") EQ "yahoo.com"
)>
<cfthrow
message="Email address must be at yahoo.com"
type="Applications"
errorcode="NotYahooEmail">
</cfif>
</cfif>
<!--- More logic here --->
</cffunction>
While it is appropriate for .cfm to report errors to a user by writing HTML to the page, it is not appropriate for a CFC method to do so. A CFC method should return an exception to the developer via CFTHROW.
For the sake of example, I will say that this CFC is stored in Application.EmploymentApp.
Here is how this form would be set up to pass the form fields to this method on submit:
CFC_Component="#Application.EmploymentApp#"
CFC_Method="saveApplication">
<cf_sebField type="text" name="fName" label="First Name" required="true">
<cf_sebField type="text" name="lName" label="Last Name">
<cf_sebField type="YahooMail" name="email" label="Yahoo! Email Address">
<cf_sebField type="submit" label="Save">
</cf_sebForm>
This would pass each form field into the "saveApplication" method of the Application.EmploymentApp component. Unfortunately, the exception would still be returned to the user as an error. Fortunately, one attribute with cf_sebForm will allow it to catch the exception as though it were a validation error:
CFC_Component="#Application.EmploymentApp#"
CFC_Method="saveApplication"
CatchErrTypes="Applications">
<cf_sebField type="text" name="fName" label="First Name" required="true">
<cf_sebField type="text" name="lName" label="Last Name">
<cf_sebField type="text" name="email" label="Yahoo! Email Address">
<cf_sebField type="submit" label="Save">
</cf_sebForm>
See it in action. Note that you can use the "forward" attribute of cf_sebForm to determine where it send the user after it takes an action this defaults to returning to the same page.
The "CatchErrTypes" attribute can hold a comma-delimited list of exception types. If the method to which cf_sebForm submits returns an error of a type in that list, cf_sebForm will return the form to the user and display the error just as it would any other validation error.
This follows the guidelines that Ben Nadel wisely laid out while keeping our form easy for us to manage and the user to complete.
The cf_sebForm (tag docs) custom tag is part of the sebtags custom tag set which is open source and free for any use.
Is the error information available via the sebform structure?
That information isn't currently available in the sebForm structure (a topic that I have yet to cover on this blog), but you are right that it should be. I will add that to my list of enhancements for the next build.
Thanks for the good suggestion!