ColdFusion Basics : Nesting Custom Tags

Posted September 26, 2006 at 7:07 PM

Tags: ColdFusion

I am posting this here to help someone on the House of Fusion CF-Talk mailing list. This demonstrates a really simple nesting custom tag example. In this demo, there are two custom tags: list.cfm and item.cfm. The item.cfm tags go in the list.cfm to create either a horizontal or vertical layout:

 Launch code in new window » Download code as text file »

  • <!--- Our parent list tag. --->
  • <cf_list align="horizontal">
  •  
  • <!--- A sub tag: item.cfm. --->
  • <cf_item>
  • Ashley Thomas
  • </cf_item>
  •  
  • <!--- A sub tag: item.cfm. --->
  • <cf_item>
  • Sarah Vivenzio
  • </cf_item>
  •  
  • </cf_list>

Before we get into the parent tag, let's explore the child tag first. Actually before we do that, let's just cover some nested tag basics:

  1. A tag can generate content if it has a close tag. That content is available in a variable THISTAG.GeneratedContent.
  2. The generated content of a tag is not available until the tag is running in execution mode: End. Think about it, if we are in the start tag, no content could possibly have been generated.
  3. Child tags associate themselves with parent tags.
  4. A parent tag DOES NOT know about its child (nested) tags until it is in its own End mode execution.

Ok, that being said, let's take a look at the child tag:

 Launch code in new window » Download code as text file »

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!---
  • We only want to associate tag info with the parent in the
  • first run of the sub tag. Otherwise, we might end up
  • storing the info twice. Also, no need to validate
  • attributes twice, the first time is sufficient.
  • --->
  • <cfif (THISTAG.ExecutionMode EQ "Start")>
  •  
  • <!---
  • Associate this tag with the parent tag. We are
  • going to override the default value for data
  • collection. It is usually AssocAttribs, which is a
  • horrible name. We are going to store it in the
  • structure "Items". This will allow the attribute data
  • of the tag to be stored in parent tag.
  •  
  • In this case, "list.cfm" is our parent tag. We define
  • this association via the old school name "cf_list".
  • --->
  • <cfassociate
  • basetag="cf_list"
  • datacollection="Items"
  • />
  •  
  • <!--- Param tag attributes. --->
  • <cfparam
  • name="ATTRIBUTES.value"
  • type="string"
  • default=""
  • />
  •  
  • <!---
  • Param the trim value flag. We are defaulting this
  • to true. In that case, we will trim the value of
  • the GENERATED CONTENT (not the Value attribute).
  • --->
  • <cfparam
  • name="ATTRIBUTES.trimvalue"
  • type="boolean"
  • default="true"
  • />
  •  
  • </cfif>
  •  
  • <!---
  • Check to see if this tag as a closing tag. If it does,
  • then we might be sending the value of the generated
  • content instead of the value attribute.
  • --->
  • <cfif THISTAG.HasEndTag>
  •  
  • <!---
  • This tag might just be self closing (which would
  • be considered a closing end tag. In that case, we
  • won't have any length in our generated content
  • (the content between the opening and closing tags.
  • Check the length of the generated contet.
  • --->
  • <cfif Len( THISTAG.GeneratedContent )>
  •  
  • <!---
  • Since we have generated content, we are going
  • to be using that as our list item value.
  • Check to see if we need to trim this.
  • --->
  • <cfif ATTRIBUTES.trimvalue>
  •  
  • <!---
  • Trim value and save it into the attributes.
  • We don't need to store into attributes, but
  • we already have the value, so why not.
  • --->
  • <cfset ATTRIBUTES.value = THISTAG.GeneratedContent.Trim() />
  •  
  • <!--- Erase the generated content. --->
  • <cfset THISTAG.GeneratedContent = "" />
  •  
  • </cfif>
  •  
  • </cfif>
  •  
  • </cfif>
  •  
  • </cfsilent>

I am not going to comment too much here because the code sample itself is very well commented. The one thing I will touch upon is the CFAssociate tag. I am hard coding the parent tag "cf_list". This does NOT need to be hard coded. You can use the GetBaseTagList() method to make it more dynamic:

 Launch code in new window » Download code as text file »

  • <cfassociate
  • basetag="#ListLast( GetBaseTagList() )#"
  • datacollection="Items"
  • />

I would NOT recommend this as you never really know where the parent tag name will show up in the base tag list. I am just noting this so you can let your imagination run wild.

Ok, now let's take a look at the parent tag:

 Launch code in new window » Download code as text file »

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!---
  • Check to see if we are in the start mode of the tag. There
  • is no need to param any attributes after the start mode.
  • --->
  • <cfif (THISTAG.ExecutionMode EQ "Start")>
  •  
  • <!---
  • Param the align attribute. This till determine if we
  • show the list one after another in-line, or if the
  • list should be displayed as block elements.
  •  
  • Possible values:
  • - horizontal (default)
  • - vertical
  • --->
  • <cfparam
  • name="ATTRIBUTES.align"
  • type="string"
  • default="horizontal"
  • />
  •  
  • </cfif>
  •  
  • </cfsilent>
  •  
  • <!---
  • Since this is a parent tag that is designed to have child
  • tags, we can't really do anything until the child tags have
  • been defined. Therefore, we can only really work in the
  • End mode of execution.
  • --->
  • <cfif (THISTAG.ExecutionMode EQ "End")>
  •  
  • <!---
  • ASSERT:
  • At this point, we should have all the child tags
  • associated with this parent tag. As per the child tags,
  • all the attribute data should be in a structure:
  • THISTAG.Items.
  • --->
  •  
  • <!---
  • Now, we have to check to see how to display the items.
  • Vertically or horizontally?
  • --->
  • <cfif (ATTRIBUTES.align EQ "vertical")>
  •  
  • <!--- Display veritcally. --->
  •  
  • <!---
  • Loop over the Items array. Remember, this array
  • contains the attributes of the child tags. Remember
  • to use CFOutput tags as custom tags are NOT natural
  • CFOutput blocks.
  • --->
  • <cfoutput>
  •  
  • <cfloop
  • index="intI"
  • from="1"
  • to="#ArrayLen( THISTAG.Items )#"
  • step="1">
  •  
  • <!--- Output value. --->
  • <p>
  • Item #intI#: #THISTAG.Items[ intI ].value#
  • </p>
  •  
  • </cfloop>
  •  
  • </cfoutput>
  •  
  • <cfelse>
  •  
  • <!---
  • We are going with the default, which is vertical.
  • You might think the first CFIF clause should be the
  • default statement since the default is probably the
  • most often used. By making the default the ELSE
  • clause, we don't really have to validate the types
  • passed in. But that's not really here nor there.
  • --->
  •  
  • <!---
  • Loop over the Items array. Remember, this array
  • contains the attributes of the child tags. Remember
  • to use CFOutput tags as custom tags are NOT natural
  • CFOutput blocks.
  • --->
  • <cfoutput>
  •  
  • <cfloop
  • index="intI"
  • from="1"
  • to="#ArrayLen( THISTAG.Items )#"
  • step="1">
  •  
  • <!--- Output value. --->
  • Item #intI#: #THISTAG.Items[ intI ].value#
  •  
  • </cfloop>
  •  
  • </cfoutput>
  •  
  • </cfif>
  •  
  • </cfif>

Again, the code above is fairly well commented, so I will just let you take it in and process it. I know that the code samples here are not great, I will try to wrap this thing up in a ZIP and put it in the ColdFusion code snippets.

Download Code Snippet ZIP File

Comments (4)  |  Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Keep your Web site content fresh and your overhead costs low with Savvy Content Manager

Reader Comments

Well written easy to follow example. Good work!

Posted by Dan G. Switzer, II on Sep 27, 2006 at 12:00 PM


Dan,

Thanks :) Just trying to help people learn. Yahoo for learning!

Posted by Ben Nadel on Sep 27, 2006 at 12:30 PM


Great work, not only on this article!

Posted by Thomas on Feb 13, 2007 at 9:01 AM


Thanks dude! Let me know if can help you with anything (demos, reviewing code, whatever).

Posted by Ben Nadel on Feb 13, 2007 at 9:05 AM


Post Comment  |  Ask Ben


Home   |   Web Log   |   ColdFusion   |   Projects   |   Resume   |   Job Form   |   Search   |   Contact
Epicenter Consulting - Custom Software Solutions for Business Evolution HostMySite.com - The Leader In ColdFusion Hosting