Improving the Tutorial (Again): Including Partials to DRY Up Forms in CFWheels

August 4, 2010 · Chris Peters

I've refined my approach for DRYing up forms in CFWheels.

This past week, Kenneth Barrett asked a question on Twitter about how to DRY up his form code in CFWheels. He didn’t want to have a copy of the same form across 2 view files. This is because he is a smart guy, even as a beginner! :)

I linked Kenneth to a post that I wrote a year ago about combining 2 forms into a single view file. That approach works well for certain scenarios, but I’ve since changed my mind on my general preference for DRYing up forms.

How the Previous Approach Affects Workflow

Really, what’s caused my change of heart is driven by how my mind works when I’m maintaining an application. It’s important to constantly be thinking about this so we can make the boring stuff (maintenance) more efficient so we can spend more time on the fun stuff (innovation! creating new things!).

When I’m maintaining my CFWheels application, I am sometimes in “view mode” or “data mode.” That will change the perspective on how I’m thinking about and approaching my application. If I’m in “view mode,” I’ll often start digging around in the files in the views folder without any regard for the CFCs stored in the controllers or models folders.

So if I want to change what happens on the form at views/users/add.cfm, that’s what I’m going to look for. Personally, I find that it’s annoying if I don’t find that particular view template and need to open the controller file to figure out what’s going on.

New Approach: Partials

Now I usually create partials to represent the pieces of code that need to be represented across several view templates. As you’ll see, this eliminates a lot of unneeded logic in the parent view templates as well as removing the need to set up so many variables in the controller actions.

Here is the new views/users/add.cfm with a partial call:

<cfparam name="user">
<cfoutput>
<h1>Create a New User</h1>
<cfif flashKeyExists("success")>
<p class="success">#flash("success")#</p>
</cfif>
#startFormTag(action="create")#
#includePartial("form")#
<div>
#submitTag(value="Create")#
</div>
#endFormTag()#
</cfoutput>
view raw add.cfm hosted with ❤ by GitHub

And now that I’m passing the user object to includePartial(), CFWheels will auto-include the partial at views/users/_form.cfm:

<cfparam name="user">
<cfoutput>
#textField(objectName="user", property="name", label="Name")#
#textField(objectName="user", property="email", label="Email")#
#passwordField(objectName="user", property="password", label="Password")#
</cfoutput>
view raw _form.cfm hosted with ❤ by GitHub

This is a lot simpler than what I had morphed the view template into in my original refactoring. There were way too many parameters, abstractions, and pieces of logic to unpack each time I revisited the template. Yuck!

So now I can change the template at views/users/edit.cfm to also include the partial and proudly display the other elements that make it unique in its own separate template (which includes a different <h1>, form action, button label, and the need to also provide a hidden field for id):

<cfparam name="user">
<cfoutput>
<h1>Edit User</h1>
<cfif flashKeyExists("success")>
<p class="success">#flash("success")#</p>
</cfif>
#startFormTag(action="update")#
#includePartial(user)#
<div>
#hiddenField(objectName="user", property="id")#
#submitTag()#
</div>
#endFormTag()#
</cfoutput>
view raw edit.cfm hosted with ❤ by GitHub

What does the controller code look like then? Well, now it’s back to the simplicity from the original tutorial:

<cffunction name="add">
<cfset user = model("user").new()>
</cffunction>
<cffunction name="edit">
<cfset user = model("user").findByKey(params.key)>
</cffunction>
view raw Users.cfc hosted with ❤ by GitHub

Hooray for simplicity!

Experience Is the Best Teacher

Sometimes you don’t really know how to handle some of these scenarios until you’ve played around with them a little. My first approach (the one I’m criticizing in this tutorial) is certainly a valid one and may make sense in certain scenarios, perhaps outside the context of forms.

I encourage you to try different approaches and share them with others so we can keep an open discussion about best practices. It takes a little humility to openly admit that I went down the wrong path earlier, but I hope that you will find some value in this discussion.

About Chris Peters

With over 20 years of experience, I help plan, execute, and optimize digital experiences.

Leave a comment