So you want to build a ModelGlue:Unity application (Part 2)

Previously in this series, we installed the ModelGlue:Unity framework and the ColdSpring framework. We used the ModelGlueApplicationTemplate as the skeleton for our new Contact-O-Matic 1.0 alpha (don't worry, we'll eventually make this a Web 2.0 application). We rejoiced when we ran our application and now will look at some essential information about our new set-up.

Let's begin with opening the Application.cfm file and giving our application a name. Set the name attribute of the application tag to 'ContactManagerMG'. If for some reason, you already have another application on your server with the name of ContactManagerMG, put another name here.

Next, as ModelGlue uses ColdSpring for configuration, let us look at some key sections of the framework config. Open the *ContactManagerMG/config/ColdSpring.xml file in your editor. There are two beans defined by default, one with the id of 'modelGlueConfiguration' and one with the id of 'reactorConfiguration'. The 'reactorConfiguration' bean is for yet another framework you could use with ModelGlue. We won't need the 'reactorConfiguration' for this series and you may delete that bean from your ColdSpring.xml file.

Ok, now look closer at the 'modelGlueConfiguration'. The property tags define how your application will behave.

view plain print about
1<bean id="modelGlueConfiguration" class="ModelGlue.unity.framework.ModelGlueConfiguration">
2        <!-- Be sure to change reload to false when you go to production! -->
3        <property name="reload"><value>true</value></property>
4        <!-- Rescaffold is overridden by reload - if reload is false, rescaffold's setting doesn't matter -->
5        <property name="rescaffold"><value>true</value></property>
6        <!-- Be sure to change debug to false when you go to production! -->
7        <property name="debug"><value>verbose</value></property>
8        <property name="defaultEvent"><value>page.index</value></property>
9        <property name="reloadPassword"><value>true</value></property>
10        <property name="viewMappings"><value>/ContactManagerMG/views</value></property>
11        <property name="generatedViewMapping"><value>/ContactManagerMG/views/generated</value></property>
12        <property name="configurationPath"><value>/ContactManagerMG/config/ModelGlue.xml</value></property>
13        <property name="scaffoldPath"><value>/ContactManagerMG/config/scaffolds/Scaffolds.xml</value></property>
14     <property name="scaffoldConfigurationPath"><value>/ModelGlue/unity/config/ScaffoldingConfiguration.xml</value></property>
15        <property name="statePrecedence"><value>form</value></property>
16        <property name="reloadKey"><value>init</value></property>
17        <property name="eventValue"><value>event</value></property>
18        <property name="defaultTemplate"><value>index.cfm</value></property>
19        <property name="defaultExceptionHandler"><value>exception</value></property>
20        <property name="defaultCacheTimeout"><value>5</value></property>
21        <property name="defaultScaffolds"><value>list,edit,view,commit,delete</value></property>
22    </bean>

The ModelGlue framework components are set and retrieved in the application scope. The framework handles this for you automatically and where historically you might have actively placed shared components and global variables into the application scope, we count on ModelGlue (and ColdSpring) to manage this for us.

For the moment, we are interested in the first four property tags; reload, rescaffold, debug and defaultEvent.

Reload tag

  • When set to 'true' will recreate our application from scratch every time and when set to the value of 'false' will run with cached objects. When developing, we will be creating and modifying a lot of the files that would be cached so we want them to be recreated each time we run a request. Set the reload tag to the value of 'true'.

    Rescaffold tag

    • Ignore for the purposes of this section.

    Debug tag

    • When set to 'true', the framework will include a set of messages about the internal actions and flow of the application. This can be set to verbose, true or false.
    Set the debug tag to the value of 'true'

    DefaultEvent tag

    • This tag sets the name of the default event. In other words, if a request comes in without a specific event, this is the event that will run. The value straight out of the box is page.index.
    Leave the value of defaultEvent set to page.index.

    ModelGlue uses a parameter called 'event' to trigger certain processes within your application. As defined in the 'modelGlueConfiguration' our application has page.index as our default. Open the *ContactManagerMG/config/ModelGlue.xml file and find the event-handler called Page.Index.

    view plain print about
    1<event-handler name="page.index">
    2        <broadcasts />
    3        <results>
    4            <result do="view.template" />
    5        </results>
    6        <views>
    7            &lt;include name="body" template="dspIndex.cfm" />

    8        </views>
    9    </event-handler>

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

    In this event-handler, there are 3 tag types; broadcasts, results and views.

    • The broadcasts tag is currently empty
    • The results tag has a child tag of result. This specific result is a command to run the event-handler for 'view.template'. If you look in the ModelGlue.xml file, you will see where the event-handler 'view.template' is defined.
    • The views tag has a child of include. In a nutshell, this is a command to run the cfm template called 'dspIndex.cfm' and store it in a value called body.

    Open the file 'dspIndex.cfm' located at *ContactManagerMG/views/dspIndex.cfm. Look in the *ContactManager/views folder and open the file named dsp.Index.cfm. Inside replace the text that says:

    view plain print about
    1Model-Glue is up and running!
    view plain print about
    1<h2>This is defined as &lt;include name="body" template="dspIndex.cfm" /></h2>

    Now run your application:
    The Body of our newly created application

    So now we see that the contents of the dspIndex.cfm file were inserted between a header and footer in our application. According to the include tag, we have this file set to the name of 'body'. Let's go see where body goes and where the rest of the html came from.

    Our page.index event-handler also fired off a result of 'view.template'. Inside the event-handler tag is a different include tag:

    view plain print about
    1<event-handler name="view.template">
    2        <broadcasts />
    3        <results />
    4        <views>
    5            &lt;include name="template" template="dspTemplate.cfm" />

    6        </views>
    7    </event-handler>

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

    The only command is an include tag with the name attribute of 'template' and template value of 'dspTemplate.cfm". Now open the file named 'dspTemplate.cfm' in the *ContactManager/views directory. Inside is the command that pulls body out of ModelGlue

    view plain print about

    Include tags place the content of the template into the viewcollection. The viewcollection holds the name and content of each include tag. Views run in order and you can set one view and pull it out in another just like we did with the body. Inside 'dspTemplate.cfm' make the following changes:

    • Change the title tag to Contact-O-Matic
    • Change the <div id="banner" tag to Contact-O-Matic
    • Change the footer to output the current date and time
    • view plain print about
      3    <title>Contact-O-Matic</title>
      4    <link rel="stylesheet" type="text/css" href="css/stylesheet.css" media="screen" />
      8    <div id="banner">Contact-O-Matic</div>
      9    <div>
      10        <div>
      11            <cfoutput>#viewcollection.getView("body")#>
      12        </div>
      13    </div>
      14    <div id="footer">
      15        <cfoutput>#dateformat( now(), "long")# #timeformat( now(), "long")# </cfoutput>
      16    </div>

      When you run your page again, you can see the beginnings of an application.
      Our Customized page

      In the debugging section, you can also see every process, in the order run by the ModelGlue framework. This is handy insight and for the next few tutorials in the series, I recommend you taking the time to read this information. Understanding how ModelGlue works will save you time when debugging your application.

      Now that we understand where all the text on the screen is coming from we shall examine the flow of the application and add a bit of functionality. The framework will run an event called onRequestStart each time a request is processed. There is a message tag defined at the top of the ModelGlue.xml file which maps the function to run on our controller.cfc located at *ContactManager/controller/controller.cfc. Each function in the controller gets a special scope called the event scope. The event scope is our central location for variables and parameters. The event has several functions available, one of which is 'getAllValues().' We will now dump the results of this function to see what the event contains.

      view plain print about
      1<cffunction name="onRequestStart" access="public" returnType="void" output="false">
      2     <cfargument name="event" type="any">
      3     <cfdump var="#event.getAllValues()#">
      4     <cfabort>
      5    <cffunction>

      When you run your Contact-O-Matic application, you can see the contents of the event scope.
      The Dump of the event.

      When a request contains URL or FORM parameters, the parameters will be available in the event also. Append '?ContactID=1' in the URL and run the application once again. http://localhost/contactmanagerMG/?ContactID=1

      You can see the value for CONTACTID is 1 in the event scope. Remove the dump and abort from onRequestStart and go into your 'dspIndex.cfm' file and place the following six lines of code.

      view plain print about
      2    <strong>Event:</strong> #viewstate.getValue("Event")#<br />
      3    <strong>ContactID:</strong> #viewstate.getValue("ContactID")#<br />
      4    <strong>ContactType:</strong> #viewstate.getValue("ContactType", "No Contact Type Defined")#<br />
      5    <strong>ContactTypes:</strong>#viewstate.getValue("ContactTypes", "There are no contact types defined either")#<br />

      Just like the controller has its own special scope (event), the views each have their scope called viewstate. All of the values in the event at the time the view was executed by the framework are in the viewstate. Run the application again. We have the name of the event being processed (our default event page.index ) We have our ContactID value from the URL. Additionally, the commands we used for 'ContactType' and 'ContactTypes' included a default value like so:

      view plain print about
      1viewstate.getValue('NameOfParameter, 'SomeDefaultValue');

      We got the default values. This works a lot like a cfparam and if we left off the default values, we would get empty strings for those not previously defined parameters.
      The values from the viewstate

      Now we have seen parameters go in the event and out of the viewstate. This is how we will pass parameters around in our ModelGlue application. Next we add the navigation and the event-handlers into the template and work on the flow of the Contact-O-Matic.

      Open the dspTemplate.cfm once again. You remember this is your global template with the header and footer. It will also contain our navigation. At the top of the page, pull the values of 'myself' and 'whichMenuIsCurrent' out of the viewstate.

      view plain print about
      1<cfset myself = viewstate.getValue("myself") />
      2<cfset whichMenuIsCurrent = viewstate.getValue("whichMenuIsCurrent", "home") />

      Next, after the closing banner div tag, add the following lines.

      view plain print about
      1<ul id="navlist">
      2        <li><a href="#myself#" <cfif whichMenuIsCurrent IS "home">id="current"</cfif>>Home</a></li>
      3        <li><a href="#myself#contact.list" <cfif whichMenuIsCurrent IS "contact.list">id="current"</cfif>>Contact List</a></li>
      4        <li><a href="#myself#contact.view" <cfif whichMenuIsCurrent IS "contact.view">id="current"</cfif>>Contact</a></li>
      5    </ul>

      Next, open the ModelGlue.xml file and copy the entire event-handler for page.index and paste it twice. Change the name of the first copy to contact.list and the name of the second to contact.view. Then split the include tag in each event-handler to have a new tag called value. The Value tag defines a simple value that will be available in the viewstate. The name attribute of each value tag will be 'whichMenuIsCurrent' and the value of each value tag will be the value to test for in the navigation. When complete, your three event-handlers should look like so:

      view plain print about
      1<event-handler name="page.index">
      2        <broadcasts />
      3            <views>
      4                &lt;include name="body" template="dspIndex.cfm">

      5                    <value name="whichMenuIsCurrent" value="home" />
      6                </include>
      7            </views>
      8            <results>
      9                <result do="view.template" />
      10            </results>
      11        </event-handler>
      13        <event-handler name="contact.list">
      14            <broadcasts />
      15            <views>
      16                &lt;include name="body" template="dspIndex.cfm">
      17                    <value name="whichMenuIsCurrent" value="contact.list" />
      18                </include>
      19            </views>
      20            <results>
      21                <result do="view.template" />
      22            </results>
      23        </event-handler>
      25        <event-handler name="contact.view">
      26            <broadcasts />
      27            <views>
      28                &lt;include name="body" template="dspIndex.cfm">
      29                    <value name="whichMenuIsCurrent" value="contact.view" />
      30                </include>
      31            </views>
      32            <results>
      33                <result do="view.template" />
      34            </results>
      35        </event-handler>

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

      Then run your application once again and click on each of the navigation links. You should see the value of event change in the URL bar as well as on the screen.

      For bonus points, we shall give a little style to our Contact-O-Matic both for looks and to reward our good progress so far. We will turn our navigation into tabs by simply adding a css file from listamatic. Copy the CSS section and place it in a new file at *ContactManagerMG/views/menu.css. Then reference the new stylesheet from within your 'dspTemplate.cfm' file like so:

      view plain print about
      1<link rel="stylesheet" type="text/css" href="css/menu.css" media="screen" />

      Now run the application once again.
      Look Ma! Tabs!
      There, now isn't that lovely? Are we having fun yet? In the next part of our series, we will add and save contacts with a contact form.

      There are no comments for this entry.

      Add Comment Subscribe to Comments

      1/23/08 4:20 AM # Posted By Rose

      Probably a disconnect between this app (it's pretty old) and the latest version(s) of MG:U. Try downloading the newer code in the sidebar labeled "Framework-Agnostic Models", which has Fusebox 5.1, MG:U and Mach-II controllers all using a single Model (ColdSpring and Reactor) and View.

      1/31/08 12:53 AM # Posted By Dave

      I currently have an application.cfc in which i have code for each of the functions inside this component, onrequeststart(), on applicationstart() etc. How can I integrate this into MG?

      Thanks in advance.

      1/31/08 12:53 AM # Posted By Chrisford

      When it comes to sessions, I break the 'rules' and directly access the Session scope in my controller CFCs. I figure this is ok since controllers really aren't the same as the model. So if I needed to do something on session start, I'd probably use onRequestStart and check to see if a particular session var existed. Not a -great- solution mind you.

      Buick Skylark Fuel Tank @

      1/31/08 12:54 AM # Posted By Dave

      Nice idea. Thanks a lot Chrisford and the rest of the guys.

      6/18/08 7:40 AM # Posted By Tassz

      Very informative and easy to follow tutorials, thanks!

      4/7/09 4:00 AM # Posted By John Gag

      Great articles! User comments helped out a lot also

      Add Comment Subscribe to Comments