Nested Custom Tags


A nested tag is a paired tag that "nests" related tags in its body. Related means related in purpose, like an HTML <form> tag and its related <input> tags.

A paired tag that surrounds one or more related tags is called a parent tag, and the related tags it surrounds are called child tags. A parent tag doesn't change its paired tag stripes just because it surrounds child tags; everything about its execution cycle remains exactly the same, ThisTag.GeneratedContent is still created and output in the proper order, and so on. The most significant difference between a paired tag that surrounds content and a paired tag that surrounds other tags is that during Inactive mode flow, control passes to the first child tag in the body of the parent. Once all the child tags have finished processing their own execution cycles, flow control passes back to the parent tag's End mode.

You already work with nested tags every day (forms and inputs, CFQUERY and CFQUERYPARAM, and so forth), and you understand how they are calledso I won't bore you with the basics of calling nested custom tags. Let's instead learn how to avoid a common but serious problem that often occurs with nested custom tags when they are called using CFMODULE syntax rather than CF_ syntax.

The CFMODULE Caveat

You'll often develop your code using CF_ syntax but have to deploy it using CFMODULE syntax, to prevent your custom tag calls from being occluded with custom tags of the same name (but different logic) used by other applications on the same shared server. You might think the worst problem you'll have is converting all those easy-to-read CF_ calls over to CFMODULE, but when most of your custom tags stop working altogether, you'll find out this is the least of your problems.

Here's why. Calling the form using CF_ syntax appears to work correctly, but it masks a potentially serious problem:

 <cf_IntelligentForm formName="MyForm">   <cf_IntelligentInput fieldName="SSN" size="12" maxLength="11">   <cf_IntelligentInput fieldName="FirstName" size="22" maxLength="20">   <cf_IntelligentInput fieldName="LastName" size="22" maxLength="20"> </cf_IntelligentForm> 

Everything looks okay on the surface, as you see in Figure 18.4, but problems lurk underneath.

Figure 18.4. This form built from custom tags appears to work correctly.


So you convert the calls to <cfmodule> syntax:

 <cfmodule template="IntelligentForm.cfm" formName="MyForm">   <cfmodule template="IntelligentInput.cfm" fieldName="SSN" size="12"     maxLength="11">   <cfmodule template="IntelligentInput.cfm" fieldName="FirstName" size="22"     maxLength="20">   <cfmodule template="IntelligentInput.cfm" fieldName="LastName" size="22"     maxLength="20"> </cfmodule> 

But now the closing <cfmodule> tag at the end of the listing doesn't know which tag to close! The result is that it closes the first unclosed tag above it, which in this case is the LastName field.

So now the DisplayField custom tag for the LastName field is being called as a closed tag, which means the LastName field is also being called as a paired tag because any tag with a closing tag is also a paired tag. This in turn means that the DisplayField custom tag for the LastName field is run twice, as you see in Figure 18.5.

Figure 18.5. Problem caused by CFMODULE's not knowing which tag to close.


So we'll solve the problem of CFMODULE not knowing which tag to close by explicitly closing all custom tag calls on the page:

 <cfmodule template="IntelligentForm.cfm" formName="MyForm">   <cfmodule template="IntelligentInput.cfm" fieldName="SSN" size="12"     maxLength="11"/>   <cfmodule template="IntelligentInput.cfm" fieldName="FirstName" size="22"     maxLength="20"/>   <cfmodule template="IntelligentInput.cfm" fieldName="LastName" size="22"     maxLength="20"/> </cfmodule> 

But now we have another problem: All the form fields are output twice! (See Figure 18.6.) The reason lies in how the child tag was coded:

 <cfparam name="Attributes.fieldName" type="string"> <cfparam name="Attributes.size" type="numeric"> <cfparam name="Attributes.maxLength" type="numeric"> <cfparam name="Attributes.label" type="string" default="#Attributes.fieldName#"> <cfoutput> <tr>   <td>#Attributes.label#&nbsp;&nbsp;</td>   <td>     <input       type="text"       name="#Attributes.fieldName#"       size="#Attributes.size#"       maxlength="#Attributes.maxlength#" >   </td> </tr> </cfoutput> 

Figure 18.6. Solving CFMODULE pairing problem uncovers another problem in cf_IntelligentInput.


Remember that closed tags are also considered to be paired tags, and paired tags are always called twiceonce in Start mode and once in End mode. Since we're not sure how the child tag will be called, we have to make sure it will work using either CF_ syntax or CFMODULE syntax, and that means outputting the child tag's contents only in Start mode. If we output only in End mode, then calling the child tag using CF_ syntax would fail because it's not called as a paired tag and therefore doesn't have an End mode.

So let's fix this in Listing 18.3.

Listing 18.3. NoComm/IntelligentInput.cfm Fixing the CFMODULE Problem
 <cfparam name="Attributes.fieldName" type="string"> <cfparam name="Attributes.size" type="numeric"> <cfparam name="Attributes.maxLength" type="numeric"> <cfparam name="Attributes.label" type="string" default="#Attributes.fieldName#"> <cfif ThisTag.ExecutionMode EQ "Start">   <cfoutput>   <tr>     <td>#Attributes.label#&nbsp;&nbsp;</td>     <td>       <input         type="text"         name="#Attributes.fieldName#"         size="#Attributes.size#"         maxlength="#Attributes.maxlength#" >     </td>   </tr>   </cfoutput> </cfif> 

Now the custom tag is working the way it really should, as you see in Figure 18.7.

Figure 18.7. Custom child tag is fixed so it can be safely called using any syntax.


So remember to always structure your custom tags from the very beginning with tests for ThisTag.ExecutionMode EQ "Start", and separate Start mode logic from End mode logic. And if your custom tag is really only meant to be called as an empty tag, make sure to execute all logic in the Start mode of the tag.

Now let's move on to the real meat of this chapter, which is the communication mechanism between parent and child tags that makes them related to one another.

Communicating Between Parent and Child Custom Tags

There are four possible models of communication between parent and child custom tags:

  • Neither tag communicates with the other.

  • The child tag communicates with the parent tag.

  • The parent tag communicates with the child tag.

  • Both tags communicate with each other.

We're going to carefully explore each of these four methods using a familiar topic: Web forms.

Nested Custom Tags That Don't Communicate

Just because parent and child custom tags are related in their functionality doesn't necessarily mean that they need to communicate with one another. In fact, the only reason for custom tags to communicate is so they can know about and use each other's data for their own purposes.

The example of IntelligentForm and IntelligentInput in Listing 18.3 is a good example of a parent and nested child custom tag that are related but don't need to know anything about each othereach tag is entirely self-contained and handles its job without needing anything from the other tag.

Figure 18.8 shows the generic relationship between two such custom tags.

Figure 18.8. The execution cycle of parent and child tags that do not communicate.


Process flow passes from the parent tag's Inactive mode to the nested child tag's Start mode as it normally does, but the child tag doesn't tell the parent tag anything about itself, and it doesn't ask the parent about the parent's data. But sharing data can lead to better functionality with less overall effort, so let's tell the parent tag about the child tag's data and see what we can do with that information.

Child to Parent

A nested child tag can send its attributes to its parent tag using the <cfassociate> tag. The parent tag receives those attributes in its ThisTag scope into an array named AssocAttribs, as Figure 18.9 shows.

Figure 18.9. The execution cycle of parent and child tags, where the child tag communicates with the parent tag.


Listing 18.4 creates a form using the same calls to IntelligentForm and IntelligentInput that you saw earlier, but the IntelligentForm tag in Listing 18.5 adds a new twist by focusing the cursor in the first form field when the field is displayedall based on its use of the child tag's data.

Listing 18.4. Child2Parent/CallingPage.cfmCreating the Form
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cf_IntelligentForm formName="MyForm">   <cf_IntelligentInput fieldName="SSN" size="12" maxLength="11">   <cf_IntelligentInput fieldName="FirstName" size="22" maxLength="20">   <cf_IntelligentInput fieldName="LastName" size="22" maxLength="20"> </cf_IntelligentForm> 

Listing 18.5. Child2Parent/IntelligentForm.cfmAn Intelligent Form Custom Tag
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cfparam name="Attributes.formName" type="string" default="formName"> <cfoutput> <cfif ThisTag.ExecutionMode EQ "Start">   <table cellspacing="0" cellpadding="0" border="0">   <form name="#Attributes.formName#" method="Post"> <cfelse>   <tr>     <td>&nbsp;</td>     <td><input type="submit" value="Submit Form"></td>   </tr>   </form>   </table>   <cfdump var="#ThisTag#" label="ThisTag as seen by the end mode of the form">   <script type="text/javascript">     #Attributes.formName#.#ThisTag.AssocAttribs[1].FieldName#.focus();   </script> </cfif> </cfoutput> 

I've placed a CFDUMP call in Listing 18.5 to show the contents of the ThisTag scope so that you can visualize what's happening. We'll look at that a little later, but for now let's see how the child tag sends its data to its parent in Listing 18.6.

Listing 18.6. Child2Parent/IntelligentInput.cfmIntelligent Form Field That Informs Its Parent
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cfparam name="Attributes.fieldName" type="string"> <cfparam name="Attributes.size" type="numeric"> <cfparam name="Attributes.maxLength" type="numeric"> <cfparam name="Attributes.label" type="string" default="#Attributes.fieldName#"> <cfif ThisTag.ExecutionMode EQ "Start">   <cfassociate basetag="CF_INTELLIGENTFORM">   <cfoutput>   <tr>     <td>#Attributes.label#&nbsp;&nbsp;</td>     <td>       <input         type="text"         name="#Attributes.fieldName#"         size="#Attributes.size#"         maxlength="#Attributes.maxlength#" >     </td>   </tr>   </cfoutput> </cfif> 

That call to <cfassociate> is what does the trick. Figure 18.10 shows what that child tag data looks like inside the parent tag.

Figure 18.10. The form showing a CFDUMP of the ThisTag scope.


When a child tag is called, the Start mode sends its Attributes scope (which, like all scopes, is a structure) to the parent tag, and the parent adds the Attributes scope structure as the value of an element in its ThisTag.AssocAttribs array.

So we can now tell on which field to focus, because it's stored in the first element of the AssocAttribs array:

 <script type="text/javascript">   #Attributes.formName#.#ThisTag.AssocAttribs[1].FieldName#.focus(); </script> 

If we comment out the call to <cfdump> and rerun the page, we get the form in Figure 18.11 with the cursor blinking in the first field, ready for input. This is a very simple example of what a parent can do with its child tags' data, but as long as you understand the basic mechanisms involved, you can figure out some rather involved ways to leverage such child tag data in your own parent tags.

Figure 18.11. Parent tag can determine the first field in which to focus the cursor, based on the child tags' data passed to it.


Now let's work from the other direction: sharing a parent tag's data with its child tags.

Parent to Child

A child tag can request its parent tag's data using the GetBaseTagData() function, which returns the parent tag's data as shown in Figure 18.12. The data returned to the child includes the parent tag's Attributes scope, its Caller scope, and its ThisTag scope as keys in a structure, as Figure 18.13 shows.

Figure 18.12. The execution cycle of parent and child tags where the parent tag communicates with the child tag.


Figure 18.13. The form showing a CFDUMP of the parentData.


Listing 18.7 looks familiar, but we've added a new inputStyle attribute that we're going to propagate down to the child tags.

Listing 18.7. Parent2Child/CallingPage.cfmCreating the Form
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cf_IntelligentForm formName="MyForm" inputStyle="color:##FF3399;">   <cf_IntelligentInput fieldName="SSN" size="12" maxLength="11">   <cf_IntelligentInput fieldName="FirstName" size="22" maxLength="20">   <cf_IntelligentInput fieldName="LastName" size="22" maxLength="20"> </cf_IntelligentForm> 

Listing 18.8 can't do anything with the child tags' data because the child tags didn't send their data to the IntelligentForm parent tag. Listing 18.9, however, makes use of its parent tag's inputStyle attribute, which is now available through the child tag's parentData structure.

Listing 18.8. Parent2Child/IntelligentForm.cfmIntelligent Form Custom Tag
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cfparam name="Attributes.formName" type="string" default="formName"> <cfparam name="Attributes.inputStyle" type="string" default=""> <cfoutput> <cfif ThisTag.ExecutionMode EQ "Start">   <table cellspacing="0" cellpadding="0" border="0">   <form name="#Attributes.formName#" method="Post"> <cfelse>   <tr>     <td>&nbsp;</td>     <td><input type="submit" value="Submit Form"></td>   </tr>   </form>   </table> </cfif> </cfoutput> 

Listing 18.9. Parent2Child/IntelligentInput.cfmIntelligent Form Field Requests Data
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cfparam name="Attributes.fieldName" type="string"> <cfparam name="Attributes.size" type="numeric"> <cfparam name="Attributes.maxLength" type="numeric"> <cfparam name="Attributes.label" type="string" default="#Attributes.fieldName#"> <cfif ThisTag.ExecutionMode EQ "Start">   <cfset parentData = GetBaseTagData("CF_INTELLIGENTFORM")>   <cfdump var="#parentData#" label="parentData as seen by #Attributes.fieldName#">   <cfoutput>   <tr>     <td>#Attributes.label#&nbsp;&nbsp;</td>     <td>       <input         type="text"         name="#Attributes.fieldName#"         size="#Attributes.size#"         maxlength="#Attributes.maxlength#"         style="#parentData.Attributes.inputStyle#">     </td>   </tr>   </cfoutput> </cfif> 

I've included a CFDUMP of the parentData structure that is the result of the GetBaseTagData() function, which is called in each child tag as shown in Figure 18.13.

The parentData structure's contents are identical in each child tag because each gets the same collection of parent tag data.

Now comment out the call to CFDUMP, rerun the page, and enter some data in each of the fields, as in Figure 18.14. Each of the fields generated by the child tags is formatted the same, according to the inputStyle attribute passed to the parent tag.

Figure 18.14. The child tags can all format themselves based on the formatting attributes passed to the parent.


Now let's combine and leverage the power of both techniques by using bidirectional communication between both tags.

Bidirectional

As you can see from Figure 18.15, all communications among related custom tags are handled by the child tag: child-to-parent communication is handled via the <cfassociate> tag, and parent-to-child communication is handled via the GetBaseTagData() function. Bidirectional communication is simply the combination of both methods.

Figure 18.15. Execution cycle of parent and child tags, in which the parent tag communicates with the child tag, and vice versa.


Listings 18.10, 18.11, and 18.12 combine the techniques shown thus far to implement bidirectional communication between parent and nested child custom tags.

Listing 18.10. Bidirectional/CallingPage.cfmCreating the Form
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cf_IntelligentForm formName="MyForm"   inputStyle="color:##003399;font-weight:bold;">   <cf_IntelligentInput fieldName="SSN" size="12" maxLength="11">   <cf_IntelligentInput fieldName="FirstName" size="22" maxLength="20">   <cf_IntelligentInput fieldName="LastName" size="22" maxLength="20"> </cf_IntelligentForm> 

Listing 18.11. Bidirectional/IntelligentForm.cfmIntelligent Form Custom Tag
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cfparam name="Attributes.formName" type="string" default="formName"> <cfparam name="Attributes.inputStyle" type="string" default=""> <cfoutput> <cfif ThisTag.ExecutionMode EQ "Start">   <table cellspacing="0" cellpadding="0" border="0">   <form name="#Attributes.formName#" method="Post"> <cfelse>   <tr>     <td>&nbsp;</td>     <td><input type="submit" value="Submit Form"></td>   </tr>   </form>   </table>   <cfdump var="#ThisTag#" label="ThisTag as seen by the end mode of the form">   <script type="text/javascript">       #Attributes.formName#.#ThisTag.AssocAttribs[1].fieldName#.focus();     </script> </cfif> </cfoutput> 

Listing 18.12. Bidirectional/IntelligentInput.cfmForm Field That Gives and Receives Data
 <!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com ---> <cfparam name="Attributes.fieldName" type="string"> <cfparam name="Attributes.size" type="numeric"> <cfparam name="Attributes.maxLength" type="numeric"> <cfparam name="Attributes.label" type="string" default="#Attributes.fieldName#"> <cfif ThisTag.ExecutionMode EQ "Start">   <cfassociate basetag="CF_INTELLIGENTFORM">   <cfset parentData = GetBaseTagData("CF_INTELLIGENTFORM")>   <cfdump var="#parentData#"     label="parentData as seen by #Attributes.fieldName#">   <cfoutput>   <tr>     <td>#Attributes.label#&nbsp;&nbsp;</td>     <td>       <input         type="text"         name="#Attributes.fieldName#"         size="#Attributes.size#"         maxlength="#Attributes.maxlength#"         style="#parentData.Attributes.inputStyle#">     </td>   </tr>   </cfoutput> </cfif> 

In Figure 18.16, it's interesting to see how each call to <cfassociate> adds another AssocAttribs entry to the parent tag's ThisTag scope, which is then returned back to the child tag via its call to GetBaseTagData().

Figure 18.16. Each child tag's call to CFASSOCIATE adds to the total available data in the parent tag, which will all eventually be returned to the last child tag via GetBaseTagData().


Figure 18.17 shows the result of running listing 18.10, which in turn calls Listings 18.11 and 18.12.

Figure 18.17. The child tags can all format themselves.


You've seen how the basic mechanisms of nested custom tags work, and I've given you some simple yet practical examples of leveraging their communication mechanisms. But the world doesn't spin on simple examples, so let's get deeper into the subject and create a realistic set of custom tags that are flexible and reusable.

NOTE

The child tags can all format themselves based on the formatting attributes passed into the parent tag and shared with the child tags, and the parent tag can determine the first field in which to focus the cursor based on the child tags' data passed to it.




Advanced Macromedia ColdFusion MX 7 Application Development
Advanced Macromedia ColdFusion MX 7 Application Development
ISBN: 0321292693
EAN: 2147483647
Year: 2006
Pages: 240
Authors: Ben Forta, et al

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net