Passing Data Between Nested Custom Tags

A key custom tag feature is the ability of collaborating custom tags to exchange complex data without user intervention while encapsulating each tag's implementation so that others cannot see it.

When you decide to you use nested tags, you must address the following issues:

What data is accessible?

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 simply documenting that the variable MyQueryResults in a tag's implementation holds an important query result set and expecting users of the custom tag to manipulate MyQueryResults directly, create another nested custom tag that manipulates MyQueryResult. This protects the users of the custom tag from changes in the tag's implementation.

Where is data accessible?

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 descendents. Ancestor data is be 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.

High-level data exchange

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 is the name of the base tag that gets access to this tag's attributes. The dataCollection attribute is 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 sub tag. 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).thisTag>

<!--- 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 CFML 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>

Ancestor data access

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:

Example: Ancestor data access

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.

To create the calling page:

  1. Create a new application page (the calling page) in ColdFusion Studio.
  2. Modify the file so that it appears as follows:
    Call cf_nestag1 which calls cf_nestag2<br>
    
    <cf_nestag1>
    
    <hr>
    
    
    
    Call cf_nestag2 directly<br>
    
    <cf_nestag2>
    
    <hr>
    
    
    
    Using a loop to call call cf_nestag2<br>
    
    <cfloop index=i from=1 to=2>
    
    <cf_nestag2>
    
    </cfloop>
    
    
  3. Save the page as nesttest.cfm.

To create the first custom tag page:

  1. Create a new application page (the calling page) in ColdFusion Studio.
  2. Put the following single line in the file:
    <cf_nestag2>
    
    
  3. Save the page as nestag1.cfm.

To create the second custom tag page:

  1. Create a new application page (the calling page) in ColdFusion Studio.
  2. Modify the file so that it appears as follows:
    <cfif thisTag.executionmode is 'start'>
    
      <!--- Get the tag context stack. The list will look something like 
    
      "CFIF,MYTAGNAME..." --->
    
      <cfset ancestorlist = getbasetaglist()>
    
    
    
      <!--- Output your own name. You are the second entry because
    
    the first entry in the context stack is the cfif tag at
    
    the top of this file --->
    
      <cfoutput>
    
      <p>I'm custom tag #ListGetAt(ancestorlist,2)#</p>
    
      
    
      <!--- output all the contents of the stack a line at a time --->
    
      <cfloop index="loopcount" from="1" to=#listlen(ancestorlist)#>
    
      Ancestorlist entry #loopcount# n is #ListGetAt(ancestorlist,loopcount)#<br>
    
      </cfloop><br>
    
      </cfoutput>
    
    
    
      <!--- Determine whether you're nested inside a loop --->
    
      <cfset inloop = listfindnocase(ancestorlist,'cfloop')>
    
      <cfif inloop neq 0>
    
        I'm running in the context of a CFLOOP tag.<p>
    
      </cfif>
    
      
    
      <!--- Determine whether you are nested inside
    
      a custom tag. Skip the first two elements of the
    
      ancestor list, i.e., CFIF and the name of the
    
      custom tag I'm in --->
    
      <cfset incustomtag = ''>
    
      <cfloop index=elem 
    
        list=#listrest(listrest(ancestorlist))#>
    
        <cfif (left(elem, 3) eq 'cf_')>
    
          <cfset incustomtag = elem>
    
          <cfbreak>
    
        </cfif>
    
      </cfloop>
    
    
    
      <cfif incustomtag neq ''>
    
        <!--- Say you are there --->
    
        <cfoutput>
    
          I'm running in the context of a custom
    
          tag named #inCustomTag#.<p>
    
        </cfoutput>
    
        
    
        <!--- Get the tag instance data --->
    
        <cfset tagdata = getbasetagdata(incustomtag)>
    
        
    
        <!--- Find out the tag's execution mode --->
    
        I'm located inside the
    
        <cfif tagdata.thisTag.executionmode neq 'inactive'>
    
          custom tag code either because it is in
    
          its start or end execution mode.
    
        <cfelse>
    
          body of the tag
    
        </cfif>
    
        <p>
    
      <cfelse>
    
        <!--- Say you are lonely --->
    
        I'm not nested inside any custom tags. :^( <p>
    
      </cfif>
    
    </cfif>
    
    
  3. Save the page as nestag2.cfm.
  4. Open the file nesttest.cfm in your browser.


Banner.Novgorod.Ru