I ran into a situation this week while working on a MachII project I’m doing with my brother that I’m sure many of you have encountered before. We have our view pages broken up into small chunks that we render inside a “pod-like” layout on the screen. Many of these view pages are using a one or more jQuery plugins (including cfUniform, DataTables and FancyBox) which need the main jQuery library to be included in the header to make them work correctly.  We had been doing something like this in the top of each view that needed jQuery:

<cfsavecontent variable="jQueryInit">
     <script type="text/javascript" language="javascript"
<cfhtmlhead text="#jQueryInit#" />

This worked perfectly fine for a while, right up until the point where we had two views in the same request that needed jQuery.  If you put the above code in each of the view pages, then only some of your jQuery plugins work because the jQuery javascript file was getting loaded and instantiated multiple times–wiping out everything that had been configured previously.

The natural first “fix” for this problem was to only include the above code in the view that would be used first on the page. While that would work in theory, it seriously breaks the notion of encapsulation that we’re trying to achieve by separating all these views into individual files.  Essentially, you could only ever use a view reliably on one page because you never knew if jQuery had been loaded or not.

I had started to contemplate building some kind of custom listener object and accompanying view to do be able to manage this problem when I remembered reading about the HTMLHelperProperty CFC included in MachII 1.8.  After doing some reading, I realized that the solution to my problem was already built right into MachII.

The HTMLHelperProperty is designed to be a “holding place” of sorts for anything that you need to dynamically add to the  section as you progress through the various steps configured by your  for the requested event. It allows you to call different functions to add javascript files, css files, and meta properties to the “pile” of things that will be inserted into the  section just before the request ends.  For example, to include the jQuery library that we need from the above example, all you have to do is add this bit of code:

<cfset getProperty("html").addJavascript("jquery-1.3.2.min.js") />

The best part is, you can add that same line to EVERY view that needs jQuery and the HTMLHelperProperty CFC will only add it to the  tag once.  Yep, you read that right! It intelligently manages duplicate additions so you don’t wind up clobbering your javascript objects with multiple invocations like we talked about above.

Now, the HTMLHelperProperty CFC isn’t confined to doing javascript files.  It also can be used to manage CSS files.  Say we have a view that needs to use the DataTables and FancyBox plugins.  Each of those plugins ships with a CSS file that it needs to make things look right. That’s no problem either, you can pass in a comma-delimited list of javascript and CSS files (or pass in an array of values as well) like this (extra line breaks added for readability–you’ll not want to use them in your code):

<cfset getProperty("html").addJavascript("
     ") />
<cfset getProperty("html").addStylesheet("
     ") />

The other use is to dynamically add  tags to your document.  These are pretty self-explanatory so I’m not going to go into them here, but refer you to the wiki page instead.

You may have noticed that there I’ve not included any file system paths to the javascript and CSS files in the examples above.  When you configure the property in your MachII config file, you can set the default “home” directories for your javascript and CSS files.  In the following example, the path to CSS files is set to “/css” and the path to javascript files is set to “/javascript”.

<!-- HTMLHelper Property -->
<property name="html" type="MachII.properties.HtmlHelperProperty">
	<parameter name="metaTitleSuffix" value=" | MyCompany, Inc." />
	<parameter name="cacheAssetPaths" value="true" />
	<!-- Defaults to ExpandPath(".") -->
	<!-- <parameter name="webrootBasePath" value="/" /> -->
	<!-- Defaults to webroot base path + "/js" -->
	<parameter name="jsBasePath" value="/javascript" />
	<!-- Defaults to webroot base path + "/css" -->
	<parameter name="cssBasePath" value="/css" />

Two quick notes on this block of code:

  1. You’ll notice that I’ve commented out the parameter named webrootBasePath. It defaults to the filesystem path equivalent to where your index.cfm page is running which is what I wanted, so I commented it out so as to use the default.
  2. You’ll also notice a parameter named “cacheAssetPaths”. There’s quite a bit behind this one, but the short version of the story is that, used correctly, this can help your visitors not have to download your javascript and css files every time (using them from the browser cache instead), while still forcing the browser to download the file if you have made any changes.  Refer to the wiki page listed above for a complete description on this feature of the HTMLHelperProperty CFC.

One thing that the HTMLHelperProperty doesn’t do at the moment is allow you to store “ad-hoc” javascript code for inclusion in the section.  This would be especially helpful in dealing with jQuery plugins as many of them need to be initialized in script once the document is loaded.  For example, the following script is required to trigger the FancyBox plugin on an image named “preview”:

$(document).ready(function() {

It would be really nice to be able to do something like the code below to have your own custom scripts included in the header after the other javascript files are loaded:

<cfsavecontent variable="fancyBoxInit">
     $(document).ready(function() {
<cfset getProperty("html").addScript( fancyBoxInit, "javascript") />

The only issue I had with the property is that I got an error under under Railo 3.1.015 if I left the .js extension off of each javascript file I wanted to include. The wiki page examples say that you can just put the name of the javascript and css files (minus the extension) into the function, but I wasn’t able to get it to work unless I included the extensions.  I’m still experimenting with this to see if it’s a bug in the property when running under Railo or if it’s something that I did.

All in all I’m very impressed with the feature and REALLY happy that I found it before tearing off and creating my own solution.

UPDATE: After looking through the code, I determined that I was getting an error when leaving off the extension of the files because my files had periods in their names other than between the filename and extension. I filed this bug and submitted a patch. The MachII team reviewed the patch, integrated it into the source and closed the ticket in the space of about 90 minutes.  So, this bug has been resolved in the latest BER version in Subversion.