Form Helpers and Showing Errors
CFWheels ties your application's forms together with your model layer elegantly. With CFWheels form conventions, you'll find yourself spending less time writing repetitive markup.
The majority of applications are not all about back-end. There is a great deal of work to perform on the front-end as well. It can be argued that most of your users will think of the interface as the application.
CFWheels is here to take you to greener pastures with its form helper functions. Let's get visual with some code examples.
Simple Example: The Old Way
Here is a simple form for editing a user profile. Normally, you would code your web form similarly to this:
Then you would write a script for the form that validates the data submitted, handles interactions with the data source(s), and displays the form with errors that may happen as a result of user input. (And most of that code isn't even included in this example.)
We know that you are quite familiar with the drudgery of typing this sort of code over and over again. Let's not even mention the pain associated with debugging it or adding new fields and business logic!
Making Life Easier: CFWheels Form Helpers
The good news is that CFWheels simplifies this quite a bit for you. At first, it looks a little different using these conventions. But you'll quickly see how it all ties together and saves you some serious time.
Rewriting the Form with CFWheels Conventions
Let's rewrite and then explain.
I know what you are thinking. 9 lines of code can't replace all that work, right? In fact, they do. The HTML output will be very nearly the same as the previous example. By using CFWheels conventions, you are saving yourself a lot of key strokes and a great deal of time.
Linking up the Form's Action with startFormTag
As we said, when linking a form to a route, there are 3 pieces of information that you will need to work with:
Route name
Parameters that the route may expect
Request method that the route requires
Most of the time, you'll probably be working with a resource. Your config/routes.cfm
may look something like this:
If you click the View Routes link in the debug footer, you'll be most interested in these types of routes for your forms:
users
GET
/users
users
index
users
POST
/users
users
create
user
PATCH
/users/[key]
users
update
user
DELETE
/users/[key]
users
delete
Once you get to this list of routes, it really doesn't matter how you authored them in your config/routes.cfm
. What matters is that you know the names, methods, and parameters that the routes expect. (With some practice, you'll probably be able to look at config/routes.cfm
and know exactly what the names, methods, and parameters are though.)
If you need to send the form via another HTTP method, you can pass that in for the method
argument as listed in your routes:
Notice above that the user
route expects a key
parameter, so that is passed into startFormTag
as the key
argument.
To drive the point home about routing parameters, let's say that we have this route:
productVariation
PATCH
[language]/products/[productKey]/variations/[key]
variations
update
As you can see, the parameters can be anything, not just primary keys.
You would link up the form like so:
A Note About PATCH and DELETE Requests
Browsers (even the modern ones) tend to only work well with GET
and POST
requests, so how does CFWheels also enable PATCH
and DELETE
requests?
To keep things secure, CFWheels will still use method="post"
on the form to send PATCH
and DELETE
requests. But the CFWheels router will recognize a PATCH
or DELETE
request if a form variable called _method
is also sent, specifying the PATCH
or DELETE
method.
So the <form>
tag generated along with a method
of patch
will look something like this:
Refactoring Common Settings with Global Defaults
Here are the settings that you would apply in config/settings.cfm
:
And here's how our example code can be simplified as a result:
All that the controller needs to provide at this point is a model object instance named profile
that contains firstName
, lastName
, and departmentId
properties and a query object named departments
that contains identifier and text values. Note that the instance variable is named profile
, though the model itself doesn't necessarily need to be named profile
.
Refactoring Label Names
Because we've named firstName
, lastName
, and departmentId
in conventional ways (camel case), CFWheels will generate the labels for us automatically:
You'll notice that CFWheels is even smart enough to translate the departmentId
property to Department
.
Form Error Messages
If you really want to secure a form, you need to do it server side. Sure, you can add JavaScript here and there to validate your web form. Unfortunately, disabling JavaScript (and thus your JavaScript-powered form validation) is simple in web browsers, and malicious bots tend not to listen to JavaScript.
Displaying a List of Model Validation Errors
CFWheels provides you with a tool set of Helper Functions just for displaying error messages as well.
The update
action may look something like this:
Let's take the previous form example and add some visual indication to the user about what he did wrong and where, by simply adding the following code on your form page.
How about that? With just that line of code (and the required validations on your object model), CFWheels will do the following:
Generate an HTML unordered list with a HTML class name of
errorMessages
.Display all the error messages on your
profile
object as list items in that unordered list.Wrap each of the erroneous fields in your form with a surrounding
<div class="fieldWithErrors">
HTML tag for you to enrich with your ninja CSS skills.
There is no longer the need to manually code error logic in your form markup.
Showing Individual Fields' Error Messages
Let's say that would rather display the error messages just below the failed fields (or anywhere else, for that matter). CFWheels has that covered too. All that it takes is a simple line of code for each form field that could end up displaying feedback to the user.
Let's add some error message handlers for the firstName
, lastName
, and departmentId
fields:
And the error messages won't even display if there aren't any. That way you can yet again use the same form code for error and non-error scenarios alike.
Types of Form Helpers
There is a CFWheels form helper for basically every type of form element available in HTML. And they all have the ability to be bound to CFWheels model instances to make displaying values and errors easier. Here is a brief description of each helper.
Text, Password, and TextArea Fields
Text and password fields work similarly to each other. They allow you to show labels and bind to model object instances to determine whether or not a value should be pre-populated.
May yield the equivalent to this HTML (if we assume the global defaults defined above in the section named Factoring out Common Settings with Global Defaults):
Hidden Fields
Would yield this type of markup:
Select Fields
Take a look at this line:
Assume that the departments
variable passed to the options argument contains a query, struct, or array of department data that should be selectable in the drop-down.
Each data type has its advantages and disadvantages:
Structs allow you to build out static or dynamic values using whatever data that you please, but there is no guarantee that your CFML engine will honor the order in which you add the elements.
Arrays also allow you to build out static or dynamic values, and there is a guarantee that your CFML engine will honor the order. But arrays are a tad more verbose to work with.
CFWheels will examine the data passed to options
and intelligently pick out elements to populate for the <option>
s' values and text.
Query: CFWheels will try to pick out the first numeric column for
value
and the first non-numeric column for the display text. The order of the columns is determined how you have them defined in your database.Struct: CFWheels will use the keys as the
value
and the values as the display text.Array: CFWheels will react depending on how many dimensions there are. If it's only a single dimension, it will populate both the
value
and display text with the elements. When it's a 2D array, CFWheels will use each item's first element as thevalue
and each element's second element as the display text. For anything larger than 2 dimensions, CFWheels only uses the first 2 sub-elements and ignores the rest.
Here's an example of how you might use each option:
When sending a query, if you need to populate your <option>
s' values and display text with specific columns, you should pass the names of the columns to use as the textField
and valueField
arguments.
You can also include a blank option by passing true or the desired text to the includeBlank
argument.
Here's a full usage with this new knowledge:
Radio Buttons
Here is an example using a query object called eyeColor
to power the possible values:
If profile.eyeColorId
's value were already set to 1
, the rendered HTML would appear similar to this:
Check Boxes
Note that binding check boxes to model objects is best suited for properties in your object that have a yes/no
or true/false
type value.
File Fields
Setting a Default Value on an Object-bound Field
Looking at this form code, it isn't 100% evident how to set an initial value for the fields:
What if we want a random title pre-filled, a certain account type pre-selected, and the check box automatically checked when the form first loads?
The answer lies in the account
object that the fields are bound to. Let's say that you always wanted this behavior to happen when the form for a new account loads. You can do something like this in the controller:
Now the initial state of the form will reflect the default values setup on the object in the controller.
Helpers That Aren't Bound to Model Objects
Sometimes you'll want to output a form element that isn't bound to a model object.
There are "tag" versions of all of the form helpers that we've listed in this chapter. As a rule of thumb, add Tag
to the end of the function name and use the name
and value
, checked
, and selected
arguments instead of the objectName
and property
arguments that you normally use.
Here is a list of the "tag" helpers for your reference:
Passing Extra Arguments for HTML Attributes
Which would produce this HTML:
When a form helper creates more than one HTML element you can typically pass in extra arguments to be set on that element as well. One common example of this is when you need to set a class
for a label
element; you can do so by passing in labelClass="class-name"
. CFWheels will detect that your argument starts with "label" and assume it should go on the label
element and not the input
element (or whatever "main" element the form helper creates). This means you could also pass in labelId="my-id"
to set the id
on the label
for example.
Boolean Attributes
HTML includes many boolean attributes like novalidate
, disabled
, required
, etc.
If you want for a CFWheels view helper to render one of these attributes, just pass the name of the attribute as an extra argument, set it to true
, and CFWheels will include the boolean attribute:
HTML5 data Attributes
data
attributes in HTML usually look something like this:
Because ColdFusion arguments cannot contain any hyphens, we have constructed a workaround for you for CFWheels view helpers.
Let's say you want a data-ajax-url
HTML attribute as depicted above. All you need to do is pass in an argument named dataAjaxUrl
, and CFWheels will convert that attribute name to the hyphenated version in the HTML output.
As an alternative, you can pass in data_ajax_url
instead if you prefer underscores, and it will produce the same result.
Special Form Helpers
CFWheels provides a few extra form helpers that make it easier for you to generate accessible fields for dates and/or times. These also bind to properties that are of type DATE, TIMESTAMP, DATETIME
, etc.
We won't go over these in detail, but here is a list of the date and time form helpers available:
Last updated
Was this helpful?