Orange is my favorite color

After seven months of heavy development and a rocky-but-now-stable launch behind me, I feel like I have some command over Model-Glue (instead of the other way around). I am certainly no expert but I want to share a few of the things that tripped me up when I was getting started and solutions that I found to the problem.

Building Forms in Pieces

Reuse is central to development and frameworks purport to aid in code reuse. In my application, there are users, organizers and superusers. Users can edit a subset of the overall data available in the database while organizers can edit almost everything. Superusers have a few additional fields at their disposal. The question is: how do you build views and events to be able to only define the form fields a single time? Oh, and don’t forget client-side Javascript validation or effects too. :)

Before Model-Glue

Before Model-Glue, I combined my CRUD into a single CFM file with some conditional handling. Here is a simple example of how my forms used to look. The ui:qform is a custom tag that outputs a SCRIPT tag and instantiates the qForms validation library. The form posted to itself and with a structKeyExists(FORM, “uidClass”) at the top, it would have handled server side validation and persistence. I really like this approach as it makes finding the right code quick and easy.

Note my conditional delete statement checking for URL.uidClass which was the convention indicating it was in “edit” mode and would show the delete link with a Javascript “are you sure…” confirmation. I don’t always use these checks but in some circumstances they make sense from a usability perspective. I also try to use the LABEL tag for accessibility and usability purposes whenever possible.

<form action="#cgi.script_name#" method="post" name="frm">

<input type="hidden" name="uidClass" value="#uidClass#" />

<table class="form">
<tr>
<td class="labelrequired"><label for="vchClass">Name</label>:</td>
<td><input type="text" name="vchClass" id="vchClass" value="#HTMLEditFormat(vchClass)#" size="25" maxlength="25" /></td>
</tr>
<tr>
<td class="label"><label for="vchClassDescription">Description</label>:</td>
<td><input type="text" name="vchClassDescription" id="vchClassDescription" value="#HTMLEditFormat(vchClassDescription)#" size="40" maxlength="255" /></td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Save Class" />
<cfif structKeyExists(URL, "uidClass")>< a href="#cgi.script_name#?uidDelete=#uidClass#" class="delete" onclick="javascript:return confirm('Are you really, really sure you want to delete this class?')">delete this class</cfif>
</td>
</tr>
</table>
</form>

<ui:qform name="frm">
// give better descriptions
objForm.vchClass.description = "Class name";

// make these fields required
objForm.required("vchClass");
</ui:qform>

Post Model-Glue Solution

In my prior system, I would sometimes abstract out everything between the FORM tags into a custom tag so that I could re-use the form in multiple contexts. This worked OK and was an approach I could have used again under Model-Glue but I wanted to try and find something more “Model-Gluey”. Specifically, I wanted to accommodate the following requirements:

  • Use the same HTML form for user, organizers and superuser editing, some of which may have different fields visible or editable
  • Forms must post to different events in each scenario
  • Validation rules (client and server-side) may be different or even optional
  • Not all instances may permit deleting the object

Each unique use of the form includes its own form tag. This is so I can pass the exit action in from model-glue.xml. The XML configuration is pretty simple:

<event-handler name="vehicle">
<broadcasts>
<message name="needVehicleMakes" />
<message name="needVehicle" />
</broadcasts>
<results>
<result do="view.render" />
</results>
<views>
<include name="formVehicle" template="shared/vehicle/frm.vehicle.cfm" />
<include name="validateVehicle" template="shared/vehicle/val.vehicle.cfm" />
<include name="main" template="staff/members/edt.vehicle.cfm">
<value name="xe.process" value="vehicle.do" />
<value name="xe.delete" value="vehicle.delete" />
</include>
</views>
</event-handler>

The val.vehicle.cfm and frm.vehicle.cfm stay the same while the edt.vehicle.cfm changes from context to context. The edt.vehicle.cfm “master” view looks something like this:

<cfset myself = viewState.getValue("myself") />
<cfset vehicle = viewState.getValue("objVehicle") />
<cfset xe.process = viewState.getValue("xe.process") />
<cfset xe.delete = viewState.getValue("xe.delete") />
<cfset frmName = viewState.getValue("frmName", "frmVehicle") />

<form action="#myself##xe.process#/uidVehicle/#vehicle.getUidVehicle()#" method="post" name="#frmName#">

< !-- bring in the HTML form, which uses its own <table> -->
#viewCollection.getView("formVehicle")#

<div class="submit">
<input type="submit" value="Save Vehicle" />
<cfif vehicle.getIsPersisted()>< a href="#myself##xe.delete#/uidVehicle/#vehicle.getUidVehicle()#" class="delete" onclick="javascript:return confirm('Are you really, really sure you want to delete this vehicle ">delete this vehicle</cfif>
</div>

</form>

< !-- bring in the validation rules (if any) -->
#viewCollection.getView("validateVehicle")#

You can see how I’m bringing in a lot of parameters from my model-glue.xml file to make it configurable without modifying the HTML making it as reusable as possible. The frm.vehicle.cfm and val.vehicle.cfm look like what you would expect, but by breaking them out into different files I can apply the appropriate validation to each scenario: admins have fewer required fields than end-users, for example.

That’s just client-side. Obviously we need to address the server-side too. If my object has different levels of validation (say, full and basic), I structure my .validate() method something like this:

<cffunction name="validate" access="public" returntype="array" output="false">
<cfreturn validateBasic() />
</cffunction>

<cffunction name="validateBasic" access="public" returntype="array" output="false">
...
</cffunction>

<cffunction name="validateFull" access="public" returntype="array" output="false">
<cfset var errors = validateBasic() />
...
</cffunction>

The controller calls the appropriate method based on context.

Bonus Advantages

What’s not so obvious is this also gives me the ability to modify what fields are available depending on context. There are several possible approaches for this including some kind of security context check wrapped in a CFIF to determine whether or not to show certain fields but there are scenarios, like sensitive financial details, where the more paranoid might opt to not include those fields in the view to prevent any possible data leakage. Both approaches are valid and in fact I use both which is why I like this method – it’s flexible!

Here’s an example view how I might combine two different views to facilitate editing additional, sensitive attributes:

<form action="#viewState.getValue("xe.formAction")#">

#viewCollection.getView("basicForm")#
#viewCollection.getView("organizerForm")#

<input type="submit" value="Save Now">
<cfif obj.getIsPersisted()>< a href="javascript;" class="delete">delete this object</cfif>

</form>

#viewCollection.getView("validateForm")#
#viewCollection.getView("organizerValidateForm")#

Future Improvements

Today, I use tables to layout my forms. Standards-weenies be damned, I think forms are a perfectly acceptable way of laying out the tabular notion of a form and they afford many conveniences (like auto-adjusting widths) that XHTML solutions can’t.

However, I’ll also admit to having fallen in love with Uniform and Quackfuzed’s tagset for generating the forms (which has a recent v2 release). My goal is to migrate to XHTML form layout (and potentially jQuery for validation… especially if Dan Switzer would just get on with it and port the ever amazing qForms to be a jQuery plugin :) ).

What About You?

How do you handle reusing forms or views in general? The idea of reuse is at the core of frameworks like Model-Glue and yet I feel like it can still be a challenge to figure out a successful strategy.

1 Comment

  1. cwxwwwxdfvwwxwx said:

    on December 24, 2008 at 3:57 pm

    well, hi admin adn people nice forum indeed. how’s life? hope it’s introduce branch ;)

{ RSS feed for comments on this post}