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

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

In this segment of our regularly scheduled programming, we look a bit deeper into ColdSpring, add the contact form and contact list.

Since I've filed this series under Rapid Development, we will be using a code generator. Peter Farrell of The Best Darn ColdFusion PodCast Period fame has made available a generator on his website that generates beans, specifically, beans used with forms. While we keep coming across the term 'bean' and it keeps signifying different things, in this case a bean refers to a component that models the form fields in a form. The bean has 'get' functions and 'set' functions corresponding to the values in the form and provides us with a handy container for form submissions.

Visit the Rooibos generator and lets make our first bean. In the first section on the left titled 'Bean Template' enter the names and types of the new formfields. For our purposes we will use the following:


view plain print about
1ContactID string
2 ContactName string
3 ContactType string

Since the bean models form submission content, and we don't trust form data, each formfield is treated as a string and we'll do our own checking later on. Fill out the next fields like so:

view plain print about
1Bean Name: ContactFormBean
2Path to Bean: ContactManagerMG.model.ContactFormBean
3Extends: (Leave blank)
4Also, check the options for getMemento() and for validate().Leave everything else unchecked.

Once you have all of the fields filled out as stated above, press the 'execute' button. On the right hand pane is the source code of your new ContactFormBean. Copy all of the code and save it in a new file named 'ContactFormBean.cfc' located in the *ContactManagerMG.model directory.

Next, we need to add the bean into our ColdSpring.xml file. Open ColdSpring.xml and add the following line after the 'modelGlueConfiguration' section.

view plain print about
1<bean id="ContactFormBean" class="ContactManagerMG.model.ContactFormBean" singleton="false" />

This configuration provides ColdSpring with the right info so it can make the component when we ask for it. The singleton='false' simply means that we want a brand new component when we request this bean, rather than the default ColdSpring behavior, which is to create the component and hold on to it for the life of the application.

Next, in your ModelGlue.xml file, find the event-handler for contact.view then split the 'broadcasts' tag to include a new child tag of 'message'. The message should have a name of 'needContactForm'

view plain print about
1<broadcasts>
2        <message name="needContactForm" />
3    </broadcasts>

While we are here, also change the template attribute of the view tag to be 'frmContact.cfm', we'll create this new file 'frmContact.cfm' in a moment.

view plain print about
1&lt;include name="body" template="frmContact.cfm">
2    <value name="whichMenuIsCurrent" value="contact.view" />
3</include>

Note: If you are copying this code, change the "& l t ;" to a proper <

Then at the top of the ModelGlue.xml file is a section for controllers. Inside the controller tag are several message-listener tags. Add a new message-listener tag with the message attribute of 'needContactForm' (same as the message being broadcast in contact.view) and the function attribute of 'getContactForm'.

view plain print about
1<message-listener message="needContactForm" function="getContactForm" />

Once you have added the new message-listener tag, open the Controller.cfc component located at *ContactManagerMG/controllers directory. Add a new function called 'getContactForm' (same name as mapped in the message-listener tag. This function will pull out the ContactFormBean from ColdSpring, use the event.makeEventBean to fill the bean with any matching values from the event scope and finally place it in the event scope.

view plain print about
1<cffunction name="getContactForm" access="public" returnType="void" output="false">
2     <cfargument name="event" type="any">
3     <cfset var ContactFormBean = getModelGlue().getBean("ContactFormBean") />
4     <cfset arguments.event.makeEventBean( ContactFormBean ) />
5     <cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />
6    </cffunction>

With me so far?, now create a new file named 'frmContact.cfm; in the *ContactManagerMG/views directory. Open the newly created 'frmContact.cfm' and paste this code:

view plain print about
1<cfset myself = viewstate.getValue("myself") />
2<cfset ContactFormBean = viewstate.getValue("ContactFormBean") />
3
4<cfoutput>
5<form id="ContactForm" action="#myself#SubmitContactForm" method="post">
6    <input type="hidden" name="contactID" value="#ContactFormBean.getContactID()#" />
7Name: <input type="text" name="ContactName" value="#ContactFormBean.getContactName()#"><br />
8Type: <input type="text" name="ContactType" value="#ContactFormBean.getContactType()#"><br />
9    <input type="submit" name="submit" value="Submit" />    
10
11</form>
12</cfoutput>
Our base form

As you can see, we pull the 'myself' and the 'ContactFormBean' out of the viewstate and use the ContactFormBean to fill the value attributes on our form fields. We are not accessing either the Form scope nor the URL scope. In place of that, we are looking in only one place for out form values, the ContactManagerBean.

After you save the file, run your application and click on the 'contact' tab. You should see your form. If by some chance you have an error, you need to review the last few steps

Now we have a form on the screen which is set to submit to an event called 'SubmitContactForm'. Lets add the event-handler tag in ModelGlue.xml for the submittal. Open ModelGlue.xml and add a new event-handler with the name of 'SubmitContactForm'. This event-handler will broadcast a message called 'contactFormSubmitted'. Additionally, we will define two named results, success and failure so we can account for the application flow.

view plain print about
1<event-handler name="SubmitContactForm">
2    <broadcasts>
3        <message name="ContactFormSubmitted" />
4    </broadcasts>
5    <results>
6        <result name="success" do="contact.list" />
7        <result name="failure" do="contact.view" />
8    </results>
9</event-handler>

Then, add the message-listener tag for 'ContactFormSubmitted' to the top section of the ModelGlue.xml file

view plain print about
1<message-listener message="ContactFormSubmitted" function="handleContactForm" />

Now create a new function in the controller to handle the contact form. For now, we will add only the 'failure' result and work with that flow.

view plain print about
1<cffunction name="handleContactForm" access="public" returnType="void" output="false">
2 <cfargument name="event" type="any">
3 <cfset var ContactFormBean = getModelGlue().getBean("ContactFormBean") />
4 <cfset arguments.event.makeEventBean( ContactFormBean ) />
5 <cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />
6 <cfset arguments.event.addResult("Failure") />
7</cffunction>

When you run your application, click on the contact tab, fill out the form and submit. You should now have a populated form. This is a typical flow when working with forms. If the form was not submitted correctly, you want to show the form populated with the original values so the user can make the necessary changes.

Our base form

Still with me so far?

We need a way to save our contacts. We are going to side-step a database for now in order to learn more about ColdSpring. ColdSpring not only creates components for you, it can also add in configuration parameters, other components and a great number of other useful features. Lets look at how we could use ColdSpring to configure an object for us. In this example, we are going to have a ContactService object. For now, the ContactService will store an array of saved contacts and also a collection of Contact Types.

Start by visiting the Rooibos Generator once more and enter in the following configuration information

In the first section on the left titled 'Bean Template' enter the names and types of the new formfields. For our purposes we will use the following:


view plain print about
1ContactTypes struct
2 ContactList array

Since the bean models form submission content, and we don't trust form data, each formfield is treated as a string and we'll do our own checking later on. Fill out the next fields like so:

view plain print about
1Bean Name: ContactService
2Path to Bean: ContactManagerMG.model.ContactService
3
4Extends: (Leave blank)
5Leave everything else unchecked.

Once you have all of the fields filled out as stated above, press the 'execute' button. On the right hand pane is the source code of your new ContactService component. Copy all of the code and save it in a new file named 'ContactService.cfc' located in the *ContactManagerMG.model directory.

Next, we need to add the bean into our ColdSpring.xml file. Open ColdSpring.xml and add the following line after the 'modelGlueConfiguration' section.

view plain print about
1<bean id="ContactService" class="ContactManagerMG.model.ContactService">
2        <property name="ContactTypes">
3            <map>
4                <entry key="Friend"><value>Friend</value></entry>
5                <entry key="Co-Worker"><value>Co-Worker</value></entry>
6                <entry key="Enemy"><value>Enemy</value></entry>
7            </map>
8        </property>
9        <property name="ContactList">
10            <list></list>
11        </property>    
12    </bean>

In ColdSpring terms, a map is a struct and a list is an array. We will use the struct to select the contact type and use the array to hold the saved contacts. Now that our ContactService.cfc is configured in ColdSpring and saved in our *ContactManagerMG.model directory, we need to add the saving mechanism to our handleContactForm function in controller.cfc. In this case, we will change the event.addResult() from 'failure' to 'success'. Edit the function to read as follows:

view plain print about
1<cffunction name="handleContactForm" access="public" returnType="void" output="false">
2 <cfargument name="event" type="any">
3 <cfset var ContactFormBean = getModelGlue().getBean( "ContactFormBean" ) />
4 <cfset var ContactService = getModelGlue().getBean( "ContactService") />
5 <cfset arguments.event.makeEventBean( ContactFormBean ) />
6 <cfset arrayAppend( ContactService.getContactList(), ContactFormBean ) />
7
8 <cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />
9 <cfset arguments.event.addResult("Success") />
10</cffunction>

This new code pulls the ContactService out of ColdSpring, appends the ContactFormBean to the array from ContactService.getContactList() . Next, add a new function to the controller.cfc called 'getContactList().

view plain print about
1<cffunction name="getContactList" access="public" returntype="void" output="false">
2 <cfargument name="event" type="any" />
3 <cfset var ContactService = getModelGlue().getBean( "ContactService") />
4 <cfset arguments.event.setValue( "ContactList", ContactService.getContactList() ) />
5</cffunction>

This function pulls the ContactService from ColdSpring and adds the ContactList to the event. Next, add a new message-listener tag that maps to this function

view plain print about
1<message-listener message="needContactList" function="getContactList" />

Now we will alter the 'contact.list' event-handler to broadcast this message. We will also write the display for the contact list and include that as well. Note the 'template' attribute of the include tag has been changed to 'dspContactList.cfm'

view plain print about
1<event-handler name="contact.list">
2    <broadcasts>
3        <message name="needContactList" />
4    </broadcasts>
5    <views>
6        &lt;include name="body" template="dspContactList.cfm">

7            <value name="whichMenuIsCurrent" value="contact.list" />
8        </include>
9    </views>
10    <results>
11        <result do="view.template" />
12    </results>
13</event-handler>

Note: If you are copying this code, change the "& l t ;" to a proper <

Lastly, we create a new .cfm file in the *ContactManagerMG.views directory called 'dspContactList.cfm'. This will pull the ContactList out of the viewstate scope and loop through with our contacts.

view plain print about
1<cfset myself = viewstate.getValue("myself") />
2<cfset ContactList = viewstate.getValue("ContactList", ArrayNew( 1 ) ) />
3
4
5<cfoutput>
6    <cfif ArrayLen( ContactList ) IS 0 >
7        -No Saved Contacts-<br />
8    <cfelse>
9        <table>
10            <tr>
11                <th>Name</th>
12                <th>Type</th>
13            </tr>    
14        <cfloop from="1" to="#ArrayLen( ContactList )#" index="x">
15            <cfset thisContact = ContactList[x] />
16            <tr>
17                <td>#thisContact.getContactName()#</td>
18                <td>#thisContact.getContactType()#</td>
19            </tr>
20        </cfloop>
21        </table>    
22    </cfif>
23</cfoutput>

Now run your application and add a contact. Once you submit the contact form, you will see the contact list with your latest submission. Are we having fun yet?

All this works nicely, until you go to another tab then click back on the ContactList. Where did the contacts go?

Since we are running in debug mode, the ModelGlue and ColdSpring objects are recreated on every request. Thus, our saved contact disappears into the ether and we get an empty array. We can persist our contacts by changing the 'reload' setting for the modelGlueConfiguration bean in ColdSpring.xml to false. This will maintain the state of our application. Try it now and add a few contacts. Works great!

Our base form

When you get bored of adding contacts, or patting yourself on the back, switch the 'reload' setting back to 'true'. Then look in the 'modelGlueConfiguration' for a the settings called reloadKey and reloadPassword. You will need to add these to the URL in order for the 'reload' setting change to take effect. The form is &reloadKey=reloadPassword. Example:

view plain print about
1http://localhost/ContactManagerMG/&event=contact.list&init=true

I recommend having a read through the ModelGlue documentation. The docs are extremely well done and are simply packed with great information. If you get stuck on a section in this Series, please leave a comment and I'll help as much as I can.

By now, you should have an appreciation for what ModelGlue:Unity can do. You should be very impressed by the power of ColdSpring. You should be proud of yourself for making it through these verbose tutorials. In following series, we will add database access, some ajax and some other interesting pieces to our Contact-O-Matic.

Download Download

There are no comments for this entry.

Add Comment Subscribe to Comments

6/18/08 10:23 AM # Posted By Tassz

Hi keep getting error: Bean definition for bean named: ContactFormBean could not be found.

Can you tell me why?


6/18/08 11:54 AM # Posted By Tassz

working on Part 3 of the tutorial. When I click on 'contact' tab - the page does not display with the objects in frmContact.cfm

Can you help?


9/23/08 6:20 PM # Posted By Chris

I get this error:

Message    Bean creation exception during init() of ContactManagerMG.model.ContactService
Detail    The CONTACTLIST argument passed to the init function is not of type array.:If the component name is specified as a type of this argument, its possible that a definition file for the component cannot be found or is not accessible.

Followed your instructions (and went over the instructions 2x). Still have this issue.

What am I missing? Really anxious to finish this tutorial...thanx!


9/23/08 8:04 PM # Posted By Dan Wilson

Chris,

I would love to help you out.

Are you sure you reloaded the application? If not, do it again.

Apart from that I don't quite know whats going on right now, but what you can do to help us is comment out the arguments, then add <cfdump var="#arguments#" /><cfabort> into the init function body, right after the commented out arguments.

Once you run the code, you can see what is being passed into the init function. Once you know, comment back and we can go from there.

DW


9/23/08 8:32 PM # Posted By Chris

Dan,

It would seem the coldspring.xml value for ContactList isn't populating the service arguments.

<property name="ContactList">
<list></list>
</property>

Using your technique to determine what's in the argument scope, I only have an empty struct. Which is getting passed into the init function properly from coldspring's xml definition.

Thoughts?

thanks!


9/23/08 8:59 PM # Posted By Dan Wilson

I'm guessing your coldspring.xml file isn't correct. Paste the entire file here: http://xml.pastebin.com then comment back.

DW


9/23/08 9:36 PM # Posted By Chris
9/23/08 10:19 PM # Posted By Dan Wilson

Ok, that all looks good. There is something going on here and I can not tell from the information available. Let's try something.

So if your ContactService component looks like mine, it should default the arguments in the init to empty struct/array values.

The actual array is being passed in via the setter, aka the setContactList method of the ContactService. So just for fun, remove both cfargument tags and change the cfset tags to simply set empty data structures. Your init will look like this:

   <cffunction name="init" access="public" returntype="ContactManagerMG.model.ContactService" output="false">
      <!--- run setters --->
      <cfset setContactTypes( structNew() ) />
<cfset setContactList( arrayNew(1) ) />


      <cfreturn this />
   </cffunction>


This will either run, or get us closer to the error in your version of the code. Lemme know what you find.

DW


9/23/08 10:28 PM # Posted By Chris

Dan,

Your suggestion worked without a hitch. Now I can add test data. No errors.


9/24/08 11:54 AM # Posted By Chris

Dan,

No errors, but now I can't save more than one record. My array isn't getting appended, it's getting overwritten. :(


9/24/08 11:57 AM # Posted By Dan Wilson

Chris,

You've probably overlooked this part of the tutorial:

All this works nicely, until you go to another tab then click back on the ContactList. Where did the contacts go?

Since we are running in debug mode, the ModelGlue and ColdSpring objects are recreated on every request. Thus, our saved contact disappears into the ether and we get an empty array. We can persist our contacts by changing the 'reload' setting for the modelGlueConfiguration bean in ColdSpring.xml to false. This will maintain the state of our application. Try it now and add a few contacts. Works great!



Try it and see...


DW


9/24/08 1:57 PM # Posted By Chris

Bingo! On with the rest of the tutorial :) thanx!


Add Comment Subscribe to Comments