The amazing adventures of Doug Hughes

Archive for March, 2010

Alagad on Facebook

In addition to following Alagad on Twitter, there is now a Facebook page! Follow us to keep up with the latest Alagad news, open source projects, and other exciting topics.


Ask an Alagadian – How can I style a cell in based on the data it contains?

Recently, someone asked the best way to style a cell inside of a <cfgrid> based on the value of the data the cell contains. As I have played around a lot with customizing <cfgrid>, this seemed like a pretty easy question to answer. I figured that we could simply use a custom renderer to wrap the value for the cell in a <div /> with a given CSS class based on the value. As it turns out, this solution worked, but was not ideal, however, a better solution was actually just as easy.
Here is the code for my original solution:

<cfajaximport />
<html>
<head>
<script>
formatStatus = function(data,cell,record,row,col,store) {
var ret = “<div class=’status”+ data +”‘>” + data + “</div>”;
return ret;
}
formatCells = function() {
theGrid = ColdFusion.Grid.getGridObject(‘widgetGrid’);
cm = theGrid.getColumnModel();
cm.setRenderer(1,formatStatus);
}
</script>
<style>
.status1{border:1px solid #000; background-color:#ffc6b8; }
.status2{border:1px solid #000; background-color:#bcb8ff; }
.status3{border:1px solid #000; background-color:#f7ffd2; }
.status4{border:1px solid #000; background-color:#abffba; }
.status5{border:1px solid #000; background-color:#ffe5bf; }
</style>
</head>
<body>
<cfset widgets = queryNew(“name,status”)>
<cfloop from=1 to=20 index=”x”>
<cfset status = randRange(1,5) />
<cfset widget = “Widget ” & x />
<cfset queryAddRow(widgets)>
<cfset querySetCell(widgets, “status”, status, x)>
<cfset querySetCell(widgets, “name”, widget, x)>
</cfloop>
<cfform name=”gridExample” style=”padding:20px”>
<cfgrid name=”widgetGrid” format=”html” query=”widgets” width=”300″ >
<cfgridcolumn name=”name” header=”Widget” />
<cfgridcolumn name=”status” header=”Status” />
</cfgrid>
</cfform>

<cfset ajaxOnLoad(“formatCells”)>
</body>
</html>

This example is based on one from Ray Camden but instead of formatting how the data is formatted, we are trying to style its ‘container’.
The call to ajaxOnLoad() makes sure that when the grid is rendered that we are calling the formatCells() JavaScript method. Inside of formatCells() we use the built-in ColdFusion JavaScript to get the instance of our grid. Remember, when we use ColdFusion.Grid.getGridObject(‘widgetGrid’) we are actually getting the native ExtJS grid object and once we have that object, we can manipulate it as we could any ExtJS grid.
Now that we have the instance of the grid object, the next thing we need to do is to get the column model using getColumnModel(). The column model is simply the column definitions for our grid. When we are using <cfgrid>, the column model is defined by our <cfgridcolumn> tags.
Once we have our column model, we can then set the renderer for the column we want to format. In this case we want to format the ‘status’ column, which is the second column defined. Since JavaScript uses zero index arrays (which I will never understand), we need to specify the second column by using ‘1’ as the first argument of setRenderer(). The second argument of setRenderer() is the JavaScript method that will be used to format our data, in this case formatStatus().
Our renderer JavaScript method accepts 6 arguments – the value, info about the cell where the data will go, which record we are looking at, the row we are in, the column we are in and the ‘store’. The store is just a JavaScript object that contains all the data for the grid.
In this first example, we are simply using JavaScript to wrap our value in a <div /> and set a CSS class based on what that status is for this record. You can also see that the status is a random value between 1 and 5 and that I have CSS styles specified for .status1, .status2, etc.
This seemed a bit too easy, and if you look at the image below, you will see that the styles worked, but the <div /> we added does not fill the entire cell, and quite frankly, I do not like the way this looks.

I figured that there has to be an equally easy way to style the actual <td> in the grid. After looking through the EXT JS documentation, it turns out there is. The second argument that is passed to our renderer, formatStatus(), has information about the cell. One of the properties available to us here is ‘css’, which allows us to add CSS classes to the actual <td> of the grid. Here is the updated code to show this new styling.

<cfajaximport />
<html>
<head>
<script>
formatStatus = function(data,cell,record,row,col,store) {
cell.css = ‘status’ + data;
return data

}
formatCells = function() {
theGrid = ColdFusion.Grid.getGridObject(‘widgetGrid’);
cm = theGrid.getColumnModel();
cm.setRenderer(1,formatStatus);
}
</script>
<style>
.status1{border:1px solid #000; background-color:#ffc6b8; }
.status2{border:1px solid #000; background-color:#bcb8ff; }
.status3{border:1px solid #000; background-color:#f7ffd2; }
.status4{border:1px solid #000; background-color:#abffba; }
.status5{border:1px solid #000; background-color:#ffe5bf; }
</style>
</head>
<body>
<cfset widgets = queryNew(“name,status”)>
<cfloop from=1 to=20 index=”x”>
<cfset status = randRange(1,5) />
<cfset widget = “Widget ” & x />
<cfset queryAddRow(widgets)>
<cfset querySetCell(widgets, “status”, status, x)>
<cfset querySetCell(widgets, “name”, widget, x)>
</cfloop>
<cfform name=”gridExample” style=”padding:20px”>
<cfgrid name=”widgetGrid” format=”html” query=”widgets” width=”300″ >
<cfgridcolumn name=”name” header=”Widget” />
<cfgridcolumn name=”status” header=”Status” />
</cfgrid>
</cfform>

<cfset ajaxOnLoad(“formatCells”)>
</body>
</html>

I think the code is cleaner and it renders much nicer than the first solution.

I plan to dive into some more of the native Ext JS functionality and plan to post some of the niftier things I find.

Regenerating ColdSpring Remote Proxies in Model-Glue Applications

A question came up on the Model-Glue mailing list today regarding forcing regeneration of ColdSpring remote proxies when reinitializing a Model-Glue application. As the solution to this is not obvious, I thought it would be good fodder for a blog post:

The first thing to understand about this is the fact that the configuration for remote proxies in ColdSpring includes a “beanFactoryName” property, and this property is used as the key in the application scope that will be used when generating the remote proxy components. In other words, if our ColdSpring config defines the beanFactoryName property as “beanfactory”, then ColdSpring is going to look for a bean factory stored in application.beanfactory — if it does not exist, it will be created.

This can be very confusing when using remote proxies in conjunction with a Model-Glue application, as no matter how many times the Model-Glue app is reinitialized, the remote proxies will not be regenerated, as they are tied to a “hidden” bean factory, not to the bean factory that is created by Model-Glue. The solution to this is to create a bean factory outside the scope of Model-Glue, and then to tell Model-Glue to use this as its parent bean factory.

In order to do this, we will directly create a bean factory instance, and load the XML config that includes our remote proxies and the components they are proxying — this is usually done in the onApplicationStart() method of Application.cfc:

<cffunction name="onApplicationStart" output="false">
    <!--- Instantiate ColdSpring bean factory --->
    <cfset beanfactory=createObject("component", "coldspring.beans.DefaultXmlBeanFactory").init() var/>

    <!--- Load ColdSpring beans & set bean factory into the application scope --->
    <cfset beanfactory.loadBeans(expandPath("/config/ParentColdSpring.xml"))/>
    <cfset application.beanfactory=beanfactory/>
</cffunction>

Next, we need to let our Model-Glue application know that it should use this as its parent bean factory. In our application’s index.cfm file, note the following code block:

<!---
**HIERARCHIAL BEAN FACTORY SUPPORT**
If you'd like to designate a parent bean factory for the one that powers Model-Glue,
simply do whatever you need to do to set the following value to the parent bean factory
instance:
<cfset ModelGlue_PARENT_BEAN_FACTORY = ??? />
--->

We’ll uncomment the <cfset> tag, and set the value to our application variable that stores the parent bean factory:

<!---
**HIERARCHIAL BEAN FACTORY SUPPORT**
If you'd like to designate a parent bean factory for the one that powers Model-Glue,
simply do whatever you need to do to set the following value to the parent bean factory
instance:
--->
<cfset ModelGlue_PARENT_BEAN_FACTORY = application.beanfactory />

At this point, once we reinitialize our Model-Glue application, it will use the bean factory we created as its parent bean factory, but reinitializing will still not cause the remote proxies to be regenerated, as the parent bean factory will not be reloaded. In order to make this happen, the parent bean factory will need to be “aware” of the Model-Glue reloadKey/reloadPassword combination when it is passed on the URL. This is usually done in the onRequestStart() method of Application.cfc:

<cffunction name="onRequestStart" output="false">
    <!--- If the MG reload key/password combo is present in the URL, reload the application --->
    <cfif "true" and is structKeyExists(url, "init") url.init>
        <cflock name="reinitApp" timeout="60" type="exclusive">
            <cfset onApplicationStart()/>
        </cflock>
    </cfif>
</cffunction>

OK, so what’s happening here? This code block will invoke the onApplicationStart() method of Application.cfc if the “init” URL parameter is present, and it is set to “true” — when onApplicationStart() is executed, it will reload the parent bean factory. Note the use of the &lt;cflock&gt; tag to single-thread the call to onApplicationStart().

Now our remote proxies will be regenerated when Model-Glue is reinitialized, but there’s still one problem: our reloadKey and reloadPassword values are now hard-coded in two places: in Application.cfc and in our main ColdSpring.xml config file. In order to change these settings, we would need to remember to make the alterations in both places.

With a little bit of additional effort, we can work around this last problem as well. We’ll make the following changes to our Application.cfc file:

<cffunction name="onApplicationStart" output="false">
    <!--- Instantiate ColdSpring bean factory & var scope variable for MG config object --->
    <cfset beanfactory=createObject("component", "coldspring.beans.DefaultXmlBeanFactory").init() var/>
    <cfset mgConfig="" var/>

    <!--- Load ColdSpring beans & set bean factory into the application scope --->
    <cfset beanfactory.loadBeans(expandPath("/config/ParentColdSpring.xml"))/>
    <cfset application.beanfactory=beanfactory/>

    <!--- Retrieve the MG config object & set the reload key/password values into the application scope --->
    <cfset mgConfig=beanfactory.getBean("modelglue.modelGlueConfiguration")/>
    <cfset application.reloadKey=mgConfig.getReloadKey()/>
    <cfset application.reloadPassword=mgConfig.getReloadPassword()/>
</cffunction>

<cffunction name="onRequestStart" output="false">
    <!--- If the MG reload key/password combo is present in the URL, reload the application --->
    <cfif and application.reloadPassword is structKeyExists(url, application.reloadKey) url[application.reloadKey]>
        <cflock name="reinitApp" timeout="60" type="exclusive">
            <cfset onApplicationStart()/>
        </cflock>
    </cfif>
</cffunction>

So, what’s different? First of all, we’re instantiating a new component in our onApplicationStart() method — this object is the internal Model-Glue configuration CFC that is populated by the values in the “modelglue.modelGlueConfiguration” bean in our primary ColdSpring.xml file. Once we have an instance of this component, we’re calling getReloadKey() and getReloadPassword() to retrieve the values set in the the config file and store them in the application scope.

Next, in onRequestStart(), we’re referencing these values in the application scope instead of the previous hard-coded values. This means that any changes made in the Model-Glue configuration will be automatically picked up, and the parent bean factory reloading will be properly initiated.

There is one final step that is required to make this work: the mentioned modelglue.modelGlueConfiguration bean definition will need to be moved from the main ColdSpring.xml file into the parent bean factory’s XML config file. This is necessary because we are calling getBean() on the parent bean factory to initialize and return this component, so it needs to be available to the parent bean factory — this will not affect Model-Glue at all, as it will simply load the configuration from the parent bean factory rather than from its own internal bean factory.

Once this has been done, reloading our application will regenerate our remote proxies, and we will only need to set the reloadKey and reloadPassword in one location.

Greetings and Salutations

As it’s customary for new Alagad bloggers to first post an introduction before diving into weightier topics, here goes:
My name is Ezra Parker, and I’ve been working with Alagad for almost three months now, initially as a contractor, and since the beginning of March, as a full-time team member. Although I’ve been developing in ColdFusion for over 10 years now, I don’t have any formal training in CS or CIS.
I began my career in web development by learning on the job while working for a company that was in the process of undergoing a transition from a pre-press service bureau into a full-fledged web development shop. I spent a number of years working there as the lead developer and technical director, and for the last year I worked as an independent contractor, eventually finding a contract with Alagad, which then resulted in my joining the team.
I’m absolutely thrilled to be working with the stellar group that Doug has assembled — it’s already been a hugely rewarding experience to collaborate with developers of this caliber, and to have the opportunity to work on challenging projects that encourage me to expand my skills and experience. I have a particular interest in all things related to the Model-Glue MVC framework, and in developing rich internet applications constructed with the jQuery and jQuery UI JavaScript libraries — expect to see a number of posts from me on both these subjects.

Development Environment on OS X

I switched to OS X shortly after joining Alagad (all the cool kids were doing it), and I have been quite pleased with both the stability and the feature-set available. I wanted to share some of the configuration tweaks I have found useful, tools I find indespensible, and configurations that have made my life easier while I write and debug ColdFusion applications on OS X.

ColdFusion Installation

I always install ColdFusion in multi-server mode. This allows me to run multiple versions of ColdFusion (even Railo) on the Jrun engine. After you run the normal install of CF with Multiserver, you can add additional ColdFusion engines from the Jrun management console, usually on port 8000. You will need to start the admin instance from the command line first, by navigating to /Applications/JRun4/bin/, then run ./jrun start admin. Once it starts, you can navigate to http://localhost:8000. The username is admin and the password will match the one you entered during the install process of ColdFusion. From here, you can choose Create New Server on the top.

Jrun management console

Once you give it a name, you can follow the deploy steps at http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=installj2ee_11.html or http://www.boyzoid.com/blog/index.cfm/2007/6/13/Running-CF8–CF-7-on-JRun-with-Apache to finish up. If you are like me, you will often have to test code on various ColdFusion engines, so I have created alias shortcuts to let me start them up quickly from the console. To setup aliases so they are available each time you go to a console, you need to edit your .profile file.

From a console, you can edit the file by typing nano ~/.profile (after entering the new lines in nano, use ctrl-x to exit the editor, and enter y to save your changes. I added the following 2 lines at the bottom of the file, so I can start either ColdFusion 8 or 9 by typing cf8-start or cf9-start.

alias cf8-start="sudo /Applications/JRun4/bin/jrun -config /configs/cf8-jvm.config start cf8"
alias cf9-start="sudo /Applications/JRun4/bin/jrun -start cfusion"

Eclipse Configuration

My workhorse editor is still Eclipse with the CFEclipse plug-in. Add the update site http://cfeclipse.org/update to Eclipse, choose the CFEclipse plugin, and you will be all set. The other required plugin, of course, is Subclipse. Subclipse allows you to check out and work with Subversion repositories, and the subclipse site is located http://subclipse.tigris.org/update_1.6.x. Your projects can live anywhere on your hard drive, just configure Apache virtual-hosts to point to the web root so you can run your tests.

Apache Configuration

The apache configuration can be updated from /etc/apache2/httpd.conf, or the vhosts file at /etc/apache2/extra/httpd-vhosts.conf. After ColdFusion is hooked into the default httpd.conf file, with CF9 as the default server, you can add a few lines into your httpd-vhosts.conf file, per v-host, to set your test site to use CF8 or Railo as needed. You have to add 2 lines to your v-host config to point it at a specific instance of your JRun server:

JRunConfig Bootstrap 127.0.0.1:51000
JRunConfig Serverstore /Applications/JRun4/lib/wsconfig/cf8/jrunserver.store

Textmate

Textmate is my go-to editor for anything not in an Eclipse project. This is an amazing editor, with bundles to do darn near anything: coldfusion, ant, XML, subversion (built in), SQL (built in), and so much more. TextMate is super fast, lightweight, and has an awesome dark color theme built right in so my eyes dont have to stare at black on white all day. If you like Notepad++ on windows, you will love TextMate on OS X.

TextMate coldfusion code syntax highlighter

I hope this helps someone out there, trying to decide if its worth making the jump to OS X. I believe it was the right move for me, and am really enjoying some of the unique tools available for the Mac.

Tag Cloud