Improving the tutorial (again): including partials to DRY up forms in Wheels
Saturday, August 14th, 2010This past week, Kenneth Barrett asked a question on Twitter about how to DRY up his form code in ColdFusion on Wheels. 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 Wheels 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(user)# <div> #submitTag(value="Create")# </div> #endFormTag()# </cfoutput>
And now that I’m passing the user object to includePartial(), Wheels will auto-include the partial at views/users/_user.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>
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>
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>
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.



August 14th, 2010 at 5:11 pm
I rewrote the page slightly. We are working on a COOP tag library for CFWheels. This will give you a preview of how to code a view page with less logic. Alternate Code Style
August 14th, 2010 at 7:51 pm
John, that’s pretty cool!
I wonder if there’s a way to write a plugin so that you don’t always need to be importing the tag library at the top of every template…
August 14th, 2010 at 9:36 pm
@Chris,
In ColdMVC, I use runtime code generation to automatically append the tag library import at the top of each view. It’s kind of a hack since you’re essentially doubling the amount of .cfm templates on the server, but the code ends up quite a bit cleaner. For example, here’s what a view ends up looking like: http://github.com/tonynelson19/ColdMVC/blob/master/samples/UserDirectory/app/views/user/edit.cfm.
August 16th, 2010 at 9:15 am
Thanks for the tip, Tony. I’m guessing we could write a plugin that adds the call to
We could add a configuration for
<cfimport>to the end of our rendering methods. It’s the beauty of Wheels plugins.prefixthat defaults to something likecfwheels,wheels, orcfw, for example.Out of curiosity, are your view helpers powered by COOP in ColdMVC, or is this your own custom tag implementation?
August 17th, 2010 at 9:59 pm
[...] I learned about the concept of partials. Chris Peters was nice enough to send me a couple of blog posts on the [...]
September 17th, 2010 at 2:27 pm
@Chris,
The view helpers that come with ColdMVC are my own implementation, although you could very easily add all of the COOP tags into a ColdMVC application simply by putting them inside the /app/tags/ folder.
May 18th, 2011 at 8:17 pm
Chris, 1st of all I’m very sorry for off topic.
2nd, many thanks for your screencasts, these lessons extremely useful for me.
I have a question related CFWheels Screencasts Episode 12: Joining Models with Associations.
There are no source code of StatusUpdates.cfc so could you correct me please:
<cfcomponent extends=”Controller” output=”false”>
<cffunction name=”add”>
<cfset params.status.personID = session.user.id>
<cfset status = model(“statuses”).new(params.status)>
<cfset status.save()>
<cfset flashInsert(success = “status updated”)>
<cfset redirectTo(controller=”main”, action=”home”)>
</cffunction>
</cfcomponent>
Is it ok? Selected line is most questionable place for me.
Best regards!
May 25th, 2011 at 10:57 am
Eldar: Looks good to me.