So you want to create a ModelGlue:Unity application? ( Part 4 )

Previously in this series, we installed the ModelGlue:Unity framework and the ColdSpring framework. We used the ModelGlueApplicationTemplate as the skeleton, added our basic flow and navigation. Finally we can save and list contacts Our Contact-O-Matic is moving right along.

In the last segment, we tested the 'failure' path and were routed back to the form. We also tested the 'success' path and were routed to the contact list. Open the ContactFormBean.cfc located in *ContactManagerMG/Model and edit the validate function (You did check the validate box in the rooibos generator like the last series said, didn't you ;) )

Our validate function should have the returntype of boolean. We will pass in a structure to hold any error messages and check the lengths of our two properties ContactName and ContactType. Your validate function should look like this:

<cffunction name="validate" access="public" returntype="boolean" output="false">
   <cfargument name="Errors" type="struct" required="true" />
   <cfset tempErrors = structNew() />
   <cfif NOT len( trim( getContactName() ) ) >
      <cfset tempErrors['ContactName'] = "Please enter a name for your contact" />
   </cfif>
   <cfif NOT len( trim( getContactType() ) )>
      <cfset tempErrors['ContactType'] = "Please enter a contact type for your contact" />
   </cfif>      
   
   <cfif structCount( tempErrors ) >
      <cfset structAppend( arguments.Errors, tempErrors ) />
      <cfreturn false />
   <cfelse>
      <cfreturn true />      
   </cfif>

</cffunction>

Perfect. We have some logic to find out if the entered data is valid. Now we add logic to the controller to only let valid data be saved. If the data is not valid, we will return the user to the form so they can fix it. Open your Controller.cfc located in *ContactManagerMG/controller and find the function named handleContactForm.

As you can see, we currently fill the ContactFormBean using makeEventBean. At that point, all matching values in the event have been loaded. We will validate right after the makeEventBean function. Our validate function takes a struct as an argument so we pass in a new struct. Then based on the return of the validate function, we either add the 'success' or the 'failure' result.

<cffunction name="handleContactForm" access="public" returnType="void" output="false">
<cfargument name="event" type="any">
<cfset var ContactFormBean = getModelGlue().getBean( "ContactFormBean" ) />
<cfset var ContactService = getModelGlue().getBean( "ContactService") />
<cfset var ContactList = ContactService.getContactList() />
<cfset var ErrorStruct = structNew () />
<cfset var isValid = false />

<cfset arguments.event.makeEventBean( ContactFormBean ) />
<cfset isValid = ContactFormBean.validate( ErrorStruct ) />

<cfif isValid >
    <cfset arrayAppend( ContactService.getContactList(), ContactFormBean ) />
    <cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />
    <cfset arguments.event.addResult("Success") />
<cfelse>
    <cfset arguments.event.setValue("ErrorStruct", ErrorStruct) />
    <cfset arguments.event.addResult("Failure") />
</cfif>
   
</cffunction>

Before you run your code, be sure to append &init=true in the URL. It is important to remember to clear the ModelGlue scope each time you change the ModelGlue.xml, ColdSpring.xml, any controller functions, or anything stored in ColdSpring.

Now when you add a contact, the contact is added to the list. If you submit a blank form, you stay on the form. This works well from a flow point of view but isn't very user friendly. We have error messages in a struct, lets put them on the screen for the user to see. Note, in the 'failure' path of the handleContactForm function, we placed the ErrorStruct in the event. The 'failure' result takes us back to the form. Thus, open 'frmContact.cfm' located at *ContactManager/Model and pull the ErrorStruct out of the event. We'll add in a default struct just to make our output cleaner

<cfset ErrorStruct = viewstate.getValue("ErrorStruct", structNew()) />

Then right above the form, check to see if the struct has any values and loop over them on the screen.

<cfoutput>
   
   <cfif structCount( ErrorStruct ) GT 0 >
      <cfloop collection="#ErrorStruct#" item="errorType">
         <li>#ErrorStruct[ errorType ]#</li>
      </cfloop>
   </cfif>
   
<form id="ContactForm" action="#myself#SubmitContactForm" method="post">
   <input type="hidden" name="contactID" value="#ContactFormBean.getContactID()#" />
Name: <input type="text" name="ContactName" value="#ContactFormBean.getContactName()#"><br />
Type: <input type="text" name="ContactType" value="#ContactFormBean.getContactType()#"><br />
   <input type="submit" name="submit" value="Submit" />   

</form>
</cfoutput>

Reload your application and try to submit the blank form. Look ma, error messages!

Hey the validation works!

You could make your validation rules as sophisticated as you like. Perhaps checking to make sure the ContactName starts with a 'D' or the ContactType is 'Friend' as we all know the business rules in an application do change. The nice part about this form of development, is a modification to the validation rules only affects the ContactFormBean. No other files need to be altered to accomplish this. Don't believe me? Lets give it a try

Say our boss comes in and demands only allowing friends into the ContactList. We simply open the ContactFormBean.cfc located at *ContactManagerMG/Model, change the ContactType rule from 'required' to 'friends only' and add an appropriate message.

Old

<cfif NOT len( trim( getContactType() ) )>
   <cfset tempErrors['ContactType'] = "Please enter a contact type for your contact" />
</cfif>

New

<cfif NOT getContactType() IS 'friend')>
   <cfset tempErrors['ContactType'] = "Only friends allow in here." />
</cfif>

Reload your application and try to add an enemy.

Only Friends allowed here!

Change it back to our original 'required' rule, save the file and Reload your application once again. Not too shabby eh?

In this series, we learned how and where to validate our form data. We also learned how to route the user based on the results of their request. The Contact-O-Matic is coming along nicely.

Comments
A really helpful series - I'm anxiously awaiting part 5!
# Posted By Holly | 1/25/07 2:09 PM
There is a piece of code with an error. When we changed the error handling code for friend and not enemy, there's an extra closing paren before closing the <cfif> tag.
# Posted By Chris Tilley | 1/29/07 4:33 PM
I know this is an old post, but I'm just getting into MG and I'm following this great tutorial.
One thing I don't understand is how the form stays populated when the validation fails and you're taken back to the form. (so if you fill in just the name and not Contact type, you're taken back to the form with the name filled in). Where is the form information coming from if you're only setting the bean into the event on "Success" and not on "Failure"??
# Posted By Tony Garcia | 7/5/07 11:28 PM
Tony,

A great question my man. Kinda seems like magic, I suppose. What we are actually doing is controlling and reusing the application flow.

See, when the form is loaded, as the event 'contact.form', a controller method called getContactForm() runs. This performs a makeEventBean() as well as setting the bean into the event scope. So any matching parameters in the event scope are added to the ContactFormBean.

When the form is submitted, the 'failure' flow of the application runs the event 'contact.form'. Since the form was submitted, the parameters are present in the event scope and will be populated into the bean.


Does that clear things up for you?

DW
# Posted By Dan Wilson | 7/6/07 9:34 AM
I think so, Dan. So basically, what you're saying is that when the "Failure" result sends you back to the contact form event, the form information is still in the event for the makeEventBean() to act upon. Is that right?
I guess what was throwing me was that I didn't know you could count on form info to stay in the event object over two events like that. I was assuming that once the contact form event was called after a failed submission, we start again with a "clean" event object (and therefore the makeEventBean() would have nothing to act upon).
So I'm assuming that the reason you set the ContactFormBean in the event object if the form DOES successfully validate is because the event that's fired in that case (contact.list), doesn't have a makeEventBean() call, right?
One other question -- why did you <cfset var ContactList = ContactService.getContactList() /> in the handleContactForm function? Because you never ended up using that ContactList variable.
I hope I'm not being a pain in the a$$. I'm really getting a lot out of your tutorial, but I want to make sure I'm understanding everything before I go on.

thanks,
T
# Posted By Tony Garcia | 7/6/07 5:10 PM
Great tutorial series Dan, even if it is getting a little old now - still as relevant as ever.

I just have a slight correction to the modified 'handleContactForm()' function.

In this bit:

<cfif isValid >
<cfset arrayAppend( ContactService.getContactList(), ContactFormBean ) />
<cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />
<cfset arguments.event.addResult("Success") />
...

The line:
<cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />

is not needed as the form is not being re-displayed (which is the reason the bean is passed back to the view - to display the submitted form data on error). Instead, on success the user is taken to the contact list page which doesn't need the ContactFormBean.
# Posted By James Allen | 11/22/07 4:21 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.001. Contact Blog Owner