Nested Custom Tags 
One ?Basic Quiz? in ColdFusion MX

Cláudio Alexandre da Costa Dias«

Custom tags are pieces of code that can be called from many places in one or more applications. Like UDFs and CFCs, they let you encapsulate code. They differ from functions, however, in how they are defined and called as well as how they share data with the calling page.
Until the release of ColdFusion MX, custom tags were the primary architectural building block for most ColdFusion applications. CFCs did not yet exist and native ColdFusion tags could not be called from within UDFs.

A CFML custom tag is simply a ColdFusion template. What makes it different from any other ColdFusion page is the way it is stored and referenced from other templates. Data can be passed out of custom tags, though not as elegantly as with CFCs.
Custom tags can be nested to and share data with each other. Thus, we can create, for example, tags <cf_table>, <cf_tr> and <cf_td> in order to apply new features to HTML tags <table>, <tr> and <td>.

In this paper we will use two custom tags: <cf_BaseCustomTag> and <cf_NestedCustomTag>. We will also use test.cfm template, which will call both custom tags. 
These three files are very simple coded, so we can easily verify and understand tag-nesting principle. Let?s have a glance on their codes:


    <cfloop index=
"i" from="1" to="5">

This file essentially calls <cf_BaseCustomTag> and, inside it ? or nested, successive calls to <cf_NestedCustomTag>
As i varies from 1 to 5, we shall have only these values as generated content.


<cfif ThisTag.ExecutionMode IS
<cfelseif ThisTag.ExecutionMode IS
     <cfif StructKeyExists(ThisTag,
         <cfset Attributes.NestedData = ThisTag.assocAttribs>
         <cfdump var=
"Nested Data">

When any custom tag executes, data related to the tag instance are kept in ThisTag structure. Its keys are listed in the following table:

Variable Description
ExecutionMode Execution mode of the tag : Start, End or Inactive
HasEndTag Does custom tag has an end tag?
GeneratedContent The HTML content generated by the tag
AssocAttribs A structure containing all of the attributes for any nested custom tags

So, inspecting previous code, we see the ThisTag.assocAttribs usage and, next, its contents output.


<cfif ThisTag.ExecutionMode IS 'Start'>
<!--- Associate local attributes to <CF_BaseCustomTag> --->    <cfassociate basetag="CF_BaseCustomTag">
<cfelseif ThisTag.ExecutionMode IS
   <cfset Attributes.content = ThisTag.GeneratedContent>
   <cfdump var=
   <cfset ThisTag.GeneratedContent =

The CFML tag <CFassociate> allows that all attributes from <cf_NestedCustomTag> be available to <cf_BaseCustomTag> in the ThisTag.assocAttribs structure.
In ThisTag.GeneratedContent, we expect to find, for each call, the numbers from 1 to 5 (as seen in test.cfm). In fact, when executing test.cfm, we have the following output given by <cf_NestedCustomTag>.

Summing up: in our example, test.cfm calls <cf_NestedCustomTag> 5 times, passing to it the numbers from 1 to 5 as content. 
<cf_NestedCustomTag>, on the other hand, passes this content to <cf_BaseCustomTag> using CFML tag <CFassociate>.

One ?Basic Quiz? in ColdFusion MX
As seen before, <cf_BaseCustomTag> will perform a dump on nested data. What should we expect? Well, the same previous output, now inside a 5-position array:

However, since the release of ColdFusion MX, the output has become:

In other words, <cf_NestedCustomTag> generates the right content, but when it is passed to <cf_BaseCustomTag>, it?s overwritten on and on. What can we do?

The Problem Analysis
First, let?s remember some ColdFusion basic concepts: 

  • Structures and Queries are accessed by reference
  • Simple variables and Arrays are accessed by value

What, in fact, does it mean?
Consider the following example:


<!--- Creates the structure --->
<cfset stEmployee = structNew()>
<!--- Sets some keys--->
<cfset = "John Doe">
<cfset stEmployee.age =
<cfdump var=
"#stEmployee#" label="stEmployee">
<!--- 'Copies' the structure --->
<cfset stNewEmployee = stEmployee>
<!--- Sets some keys--->
<cfset =
"Mary Ann">
<cfset stNewEmployee.age =
<cfdump var=
<cfdump var=
"stEmployee after 'Copy'">

The output is:

Well, in ?copy?, it was copied the reference to a memory area, not the memory area itself. Thus, any reference made to stNewEmployee would reference stEmployee. Both are pointers to the same memory area.
Changing the fake ?copy? <cfset stNewEmployee = stEmployee> to <cfset stNewEmployee = duplicate(stEmployee)>, we get what was expected:

Using duplicate() function, we got two pointers pointing to distinct memory areas.
What about nested custom tags?
In our problem, we have a similar behavior. Do you remember? Structures were overwritten as new ones were ?created?. It suggests that, when Java code is generated, that basic concept was not taken into account.
Let?s see another example:


<cfset arrMyArray = arrayNew(1)>
<cfset stTemp = structNew()>
<cfloop index=
"i" from="1" to="5">
<!--- Sets a key --->
     <cfset stTemp.content = i>
<!--- Saves the structure in array --->
     <cfset arrMyArray[i] = stTemp>
<cfdump var=
"#arrMyArray#" label="arrMyArray">

Which gives the following output:

In fact, this situation resembles our nested custom tags problem. 
Let?s see: the structure stTemp is created outside the loop and its content key is changed each step of the loop. 
When we put it into array?s i position, we are making that ?copy? first shown. In other words, we are just copying the reference, not creating a new memory area. That means: we are overwriting the structure each step of the loop. Exactly as shown when nesting custom tags.
Again, if we change the ?copy? <cfset arrMyArray[i] = stTemp> to <cfset arrMyArray[i] = duplicate(stTemp)>, we have the ?problem? fixed.

Not Fixing. Just Solving...
In the examples, we were causing the problem, because we were laying aside one basic and very important concept about structures. 
But, in nested custom tags case, the problem resides in generated Java code. We are neither able to act on it nor fix the code.
We shall, thus, wait for a new updater. 

Meanwhile, what can we do?
It was almost by accident. When I was using a pair of nested custom tags, I noticed that the problem only occurs when no attributes are passed to the custom tag inside the loop, exactly as shown in test.cfm. 
In other words, it looks like, when ColdFusion MX finds the first call to a custom tag with no attributes, it translates its code and allocates memory to store its data.
When it finds another call to the same custom tag, again with no attributes, it ?thinks?: ?if no attributes are present, nothing has changed. If nothing has changed, I will not allocate memory?. Previous generated content is then overwritten.
So, if we give the custom tag an attribute, we have no more problems. 
But, what if the custom tag does not have an attribute to be passed in? Here is the trick: Any word can be passed to the custom tag, and no values are required. 
If we change test.cfm to:


  <cfloop index=
"i" from="1" to="5">
      <cf_NestedCustomTag FOO>

Please note the addition of foo attribute to <cf_NestedCustomTag> call. Now, we have the following output:

And the problem is then solved.

Cláudio Alexandre da Costa Dias, MSc. ? clá
Senior Engineer ? EMBRAER ? Brazil
ColdFusion user since 1997

Responsible for ColdFusion MX usage and propagation in EMBRAER?s Flight-Test Division

About This Tutorial
Author: Claudio Dias
Skill Level: Intermediate 
Platforms Tested: CFMX
Total Views: 83,663
Submission Date: June 23, 2003
Last Update Date: June 05, 2009
All Tutorials By This Autor: 1
Discuss This Tutorial
  • give some example coding


Sponsored By...
Mobile App Development (IOS, Android, Cordova, Phonegap, Objective-C, Java) - Austin, Texas Mobile Apps - Touch512, LLC.