A custom tag can call other custom tags from within its body text, thereby nesting tags. ColdFusion uses nested tags such as cfgraph and cfgraphdata, cfhttp and cfhttpparam, and cftree and cftreeitem. The ability to nest tags allows you to provide similar functionality.
The following example shows a cftreeitem tag nested within a cftree tag:
<cftree name="tree1" required="Yes" hscroll="No"> <cftreeitem value=fullname query="engquery" queryasroot="Yes" img="folder,document"> </cftree>
The calling tag is known as an ancestor, parent, or base tag; the tags that ancestor tags call are known as descendant, child, or sub tags. Together, the ancestor and all descendant tags are called collaborating tags.
In order to nest tags, the parent tag must have a closing tag.
The following table lists the terms that describe the relationships between nested tags:
Calling tag |
Tag nested within the calling tag |
Description |
---|---|---|
Ancestor |
Descendant |
An ancestor is any tag that contains other tags between its start and end tags. A descendant is any tag called by a tag. |
Parent |
Child |
Parent and child are synonyms for ancestor and descendant. |
Base tag |
Sub tag |
A base tag is an ancestor that you explicitly associate with a descendant, called a sub tag, with cfassociate. |
You can create multiple levels of nested tags. In this case, the sub tag becomes the base tag for its own sub tags. Any tag with an end tag present can be an ancestor to another tag.
Nested custom tags operate through three modes of processing, which are exposed to the base tags through the variable thisTag.ExecutionMode.
A key custom tag feature is for collaborating custom tags to exchange complex data without user intervention, while encapsulating each tag's implementation so that others cannot see it.
When you use nested tags, you must address the following issues:
To enable developers to obtain maximum productivity in an environment with few restrictions, CFML custom tags can expose all their data to collaborating tags.
When you develop custom tags, you should document all variables that collaborating tags can access and/or modify. When your custom tags collaborate with other custom tags, you should make sure that they do not modify any undocumented data.
To preserve encapsulation, put all tag data access and modification operations into custom tags. For example, rather than documenting that the variable MyQueryResults in a tag's implementation holds a query result and expecting users to manipulate MyQueryResults directly, create a nested custom tag that manipulates MyQueryResult. This protects the users of the custom tag from changes in the tag's implementation.
Use the Request scope for variables in nested tags. The Request scope is available to the base page, all pages it includes, all custom tag pages it calls, and all custom tag pages called by the included pages and custom tag pages. Collaborating custom tags that are not nested in a single tag can exchange data using the request structure. The Request scope is represented as a structure named Request.
Two custom tags can be related in a variety of ways in a page. Ancestor and descendant relationships are important because they relate to the order of tag nesting.
A tag's descendants are inactive while the page is executed; that is, the descendent tags have no instance data. A tag, therefore, can only access data from its ancestors, not its descendants. Ancestor data is available from the current page and from the whole runtime tag context stack. The tag context stack is the path from the current tag element up the hierarchy of nested tags, including those in included pages and custom tag references, to the start of the base page for the request. Both cfinclude tags and custom tags appear on the tag context stack.
Although the ability to create nested custom tags is a tremendous productivity gain, keeping track of complex nested tag hierarchies can become a chore. The cfassociate tag lets the parent know what the children are up to. By adding this tag to a sub tag, you enable communication of its attributes to the base tag.
In addition, there are many cases in which descendant tags are used only as a means for data validation and exchange with an ancestor tag, such as cfhttp/cfhttpparam and cftree/cftreeitem. You can use the cfassociate tag to encapsulate this processing.
The cfassociate tag has the following format:
<cfassociate baseTag="tagName" dataCollection="collectionName">
The baseTag attribute specifies the name of the base tag that gets access to this tag's attributes. The dataCollection attribute specifies the name of the structure in which the base tag stores the sub-tag data. Its default value is AssocAttribs. You only need to specify a dataCollection attribute if the base tag can have more than one type of subtag. It is convenient for keeping separate collections of attributes, one per tag type.
When cfassociate is encountered in a sub tag, the sub tag's attributes are automatically saved in the base tag. The attributes are in a structure appended to the end of an array whose name is thisTag.collectionName.
The cfassociate tag performs the following operations:
<!--- Get base tag instance data ---> <cfset data = getBaseTagData(baseTag)> <!--- Create a string with the attribute collection name ---> <cfset collection_Name = "data.#dataCollection#"> <!--- Create the attribute collection, if necessary ---> <cfif not isDefined(collectionName)> <cfset #collection_Name# = arrayNew(1)> </cfif> <!--- Append the current attributes to the array ---> <cfset temp=arrayAppend(evaluate(collectionName), attributes)>
The code accessing sub-tag attributes in the base tag could look like the following:
<!--- Protect against no sub-tags ---> <cfparam Name='thisTag.assocAttribs' default=#arrayNew(1)#> <!--- Loop over the attribute sets of all sub tags ---> <cfloop index=i from=1 to=#arrayLen(thisTag.assocAttribs)#> <!--- Get the attributes structure ---> <cfset subAttribs = thisTag.assocAttribs[i]> <!--- Perform other operations ---> </cfloop>
The ancestor's data is represented by a structure object that contains all the ancestor's data.
The following functions provide access to ancestral data:
This example creates two custom tags and a simple page that calls each of the custom tags. The first custom tag calls the second. The second tag reports on its status and provides information about its ancestors.
Create the first custom tag page
Create the second custom tag page