Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at RIA Unleashed (Nov. 2010) with:

Using Variables And Named Templates In XSLT And ColdFusion's XmlTransform()

By Ben Nadel on
Tags: ColdFusion

Although I gave an introductory XSLT in ColdFusion presentation last week at my company, there is still much more to learn. Today, I studied up on calling named templates and using variables. In my mind, the two go hand-in-hand as you can not only use global variable but also pass variables into a named template as you would with method invocation in traditional programming.

Like traditional programming, variables can be global to the entire XSL transformation or local to a contextual template. There are two ways to declare variables:

  • Using the Variable element
  • Using the Param element

These are exactly like the CFSet and CFParam ColdFusion tags. Both set the variable value, however, the Param element (just like CFParam) only defines a default value. If the variable already exists at the time that the Param element is executed, the existing variable value is what is used (not the Param value). If you aren't going to use the paramming nature of the Param element, you should use the Variable element; while the two might be interchangeable, Variable makes a stronger statement about the intent of your code, and in my opinion, stronger statements are better statements.

Both the Variable and Param elements have two attributes: Name and Select. The Name attribute gives the variable a name that can be referenced later in the code. The Select attribute is optional and gives the variable a value. The Select attribute must be a valid XPath expression. If you leave out the Select attribute, the variable will default to an empty string. Instead of using the Select attribute, you can also use the value between the open and close tags of the Variable element.

All of the following Param element tags should result in the same thing: the variable "foo" being set to an empty string (assuming that the foo variable does not yet exist).

  • <!--- Param a value. --->
  • <xsl:param name="foo" select="''" />
  •  
  • <!--- Param a value. --->
  • <xsl:param name="foo" />
  •  
  • <!--- Param a value. --->
  • <xsl:param name="foo"><xsl:param>
  •  
  • <!--- Param a value. --->
  • <xsl:param name="foo">
  • <xsl:text></xsl:text>
  • <xsl:param>

In the above example, if we didn't care about the paramming nature of the Param element, we could have swapped xsl:param with xsl:variable and gotten the same result.

Once a variable is set, it can be reference using the dollar sign ($) followed by the name of the variable. Selecting the variable value in a Value-Of element would look like this:

  • <!--- Output variable. --->
  • <xsl:value-of select="$foo" />

If you wanted to reference the variable value as part of the attribute of another element, you could either build the element dynamically using the Element and Attribute elements, or you could simply use the curly-brace short hand. When in the attribute of an element, wrapping the variable name in curly-braces does the equivalent of an in-line Value-Of:

  • <a href="##" title="{$name}">
  • <xsl:value-of select="$name" />
  • </a>

This only works within attributes. If you put {$name} into the content of a template, it will simply output the literal text {$name}, not the variable value.

Variables can be defined within the context of the main transformation element or within the context of a template. Elements defined within the context of the main transformation element are global to the transformation and can be referenced within all templates. Variables created within the context of a template are only available within that template.

Now that we understand variables, let's take a look at named templates. A named template is merely a template that gets called by name, rather than by implicit XPath pattern matching. Instead of using the Select attribute, it uses the name attribute:

  • <xsl:template name="my-template">
  • <!--- Template code. --->
  • </xsl:template>

To invoke a named template, you use the Call-Template element rather than the Apply-Templates element:

  • <!--- Invoke the my-template template. --->
  • <xsl:call-template name="my-template" />

The Call-Template element has only one attribute, Name, the name of the template that we are executing. Again, this is very much like using the CFInvoke ColdFusion tag to invoke a ColdFusion template as a custom tag. There is one very important caveat here! When invoking a named template, the contextual node does not change as it does with implicit template matching. When you are running transformations inside of a name-invoked template, you are still in the node-context of the calling code.

In the above example, we are not passing in any value to the named template. To pass in values, we can use the With-Param element. The With-Param has the same structure of the Param element, the Name and Select attributes, and has the same optional Select behavior. Within the target template, we need to define the incoming values using the Param element.

I don't mean to gloss over calling templates and passing values, but I think it makes more sense to see it in action. To help bring it all together, I have put together this demo that uses a variety of variable declaration styles as well as a named template with a variety of invocation argument types. The ColdFusion XML document that we are transforming is rather simple; it's composed of a few girls with name and description elements:

  • <!--- Build the girls XML document object. --->
  • <cfxml variable="xmlGirls">
  •  
  • <girls>
  • <girl>
  • <name>Dakota Fanning</name>
  • <desc>
  • Dakota fanning is just a cool actress. She
  • gets a bad rep from time to time because
  • she has the maturity of someone much older
  • than herself; but come on - her level of
  • acting is very high.
  • </desc>
  • </girl>
  • <girl>
  • <name>Amy Poehler</name>
  • <desc>
  • Amy Poehler has such a quicky sexiness. At
  • first, I wasn't all that attracted to her,
  • but after watching Saturday Night Live a
  • bunch of times, you just realize how much
  • of cutie she is.... When she goes to a party,
  • she brings a good personaly, fun times, and
  • one very sexy leg!
  • </desc>
  • </girl>
  • <girl>
  • <name>Karyn Dwyer</name>
  • <desc>
  • I've only seen her in one movie - Better
  • Than Chocoloate - opposite of Christina Cox.
  • While I generally obsess over Christina,
  • certainly, Karyn can hold her own with an
  • angelic cuteness.
  • </desc>
  • </girl>
  • </girls>
  •  
  • </cfxml>

Now, we are going to take that ColdFusion XML document and transform it into a really basic XHTML page that loops over the girls and outputs the name and description. Notice that for each girl, we are going to be invoking a named template and passing in the Name and Description "Arguments":

  • <!--- Build the transformation document object. --->
  • <cfxml variable="xmlTransform">
  •  
  • <!--- Document type declaration. --->
  • <?xml version="1.0" encoding="ISO-8859-1"?>
  •  
  • <xsl:transform
  • version="1.0"
  • xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  •  
  •  
  • <!---
  • Set a global variable to store the max number of
  • girls we can show on the rendered page.
  • --->
  • <xsl:variable
  • name="max-girl-count"
  • select="2"
  • />
  •  
  • <!---
  • Set a global variable for the title of the page. I
  • am doing this to demonstrate the use of a nested
  • "value", rather than a selected one.
  • --->
  • <xsl:variable name="page-title">
  • <xsl:text>Girls In Movies</xsl:text>
  • </xsl:variable>
  •  
  •  
  • <!--- BEGIN: Templates ---------------------------- --->
  •  
  •  
  • <!---
  • Match the root of the XML document. Here, we are
  • going to output the girls using a named template
  • rather than explicit node-template matching.
  • --->
  • <xsl:template match="/">
  •  
  • <!---
  • Output the doc type. Since we cannot output a
  • doc type in the XSLT, we need to escape it and
  • then tell the Text element not to escape the
  • rendered ampersands.
  • --->
  • <xsl:text disable-output-escaping="yes">
  • &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
  • </xsl:text>
  •  
  • <html>
  • <head>
  • <title>
  • <!--- Reference to global variable. --->
  • <xsl:value-of select="$page-title" />
  • </title>
  • </head>
  • <body>
  •  
  • <h1>
  • <!--- Reference to global variable. --->
  • <xsl:value-of select="$page-title" />
  • </h1>
  •  
  • <!---
  • Loop over the girls to output them. We
  • are only going to output the MAX amount of
  • girl allowed; in order to accomplish this,
  • we are going to make sure that the
  • contextual girl's position in the sibling
  • nodes it NO greater than the value heald in
  • the global variable.
  • --->
  • <xsl:for-each
  • select="//girl[ position() &lt;= $max-girl-count ]">
  •  
  • <!---
  • Call the girl display template and pass
  • in the name text value.
  • --->
  • <xsl:call-template name="girl-display">
  •  
  • <!---
  • Send the name Text() value as the
  • overriding argument (to override the
  • param'ed argument).
  • --->
  • <xsl:with-param name="name">
  • <xsl:value-of select="name/text()" />
  • </xsl:with-param>
  •  
  • <!---
  • Send in the description. Instead
  • of passing it in via a tested value,
  • just use the Select attribute.
  • --->
  • <xsl:with-param
  • name="description"
  • select="desc/text()"
  • />
  •  
  • </xsl:call-template>
  •  
  • </xsl:for-each>
  •  
  • </body>
  • </html>
  •  
  • </xsl:template>
  •  
  •  
  • <!---
  • This template can handle the formatting of a girl.
  • Insteadigng of just passing in the girl node, we
  • are going to pass in some paramed variables.
  • --->
  • <xsl:template name="girl-display">
  •  
  • <!---
  • Param the passed in varaible. Here, we are
  • setting default values that can be overriden
  • by passed in variables. Here, we need to use
  • the empty string in single quotes because the
  • Select attribute cannot be empty.
  • --->
  • <xsl:param name="name" select="''" />
  •  
  • <!---
  • Paraming can also be done then nested value
  • (as opposed to the Select attribute).
  • --->
  • <xsl:param name="description"></xsl:param>
  •  
  •  
  • <h3>
  • <xsl:value-of select="$name" />
  • </h3>
  •  
  • <!--- Ouptut the descriptoin. --->
  • <p>
  • <xsl:value-of select="$description" />
  • </p>
  •  
  • <p>
  • <!---
  • When outputting the A-tag, we are going to
  • put the name of the girl as the title of
  • the link. To do this, we can simply wrap
  • the variable name in curly braces {$var}.
  • This allows us to build dynamic attributes
  • without using the Element / Attribute
  • elements.
  • --->
  • <a
  • href="##"
  • title="{$name}"
  • >Read More</a>
  •  
  • <!---
  • When outputting the right-angle-quote, we
  • need to disable output escaping otherwise
  • the ampersand will be escaped by default.
  • --->
  • <xsl:text
  • disable-output-escaping="yes">
  • &amp;raquo;
  • </xsl:text>
  • </p>
  •  
  • </xsl:template>
  •  
  • </xsl:transform>
  •  
  • </cfxml>
  •  
  •  
  • <!---
  • Output the transformed XML page data into an
  • HTML formatted web page using XSLT.
  • --->
  • #XmlTransform(
  • xmlGirls,
  • xmlTransform
  • )#

Running the above transformation, we get the following XHTML page (the yellow tool-tip is the Title attribute that was dynamically generated using the {$var} notation):


 
 
 

 
XHTML Page Generated Using ColdFusion, XSLT, Named Templates, And Variables  
 
 
 

Named templates and variables are really useful, especially when use together. I am not sure how much more in-depth I want to go with XSLT. I feel like with my "core" understanding, it might be more worthwhile to look up specific uses of XSLT (case studies) rather than trying to learn the entire set of tags and methods. That way, I can get a good feel for when it might be best to use XSLT to solve certain types of problems.




Reader Comments

Slightly OT, but any specific reason you are using ISO-8859-1 as opposed to the XML default UTF-8? The only reason I even noticed it because thought adding the xml declaration inside <cfxml> would throw an error, but apparently support was added in mx7.

Reply to this Comment

@Dustin,

I have it there simply because it was in the example that I learned from :) As you can see, I don't put a doctype in the original XML document. I guess I could remove it from the transformation document as well - I hadn't thought about it.

Reply to this Comment

@Matt,

Thanks! I didn't even know that was an option of the Output element. Good stuff to know. What you are doing seems like a much cleaner solution.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.