Associating Parent/Child Custom Tags (UPDATED!)

Posted by Dan on Dec 19, 2006 @ 10:23 AM
UPDATE:
Earlier today I posted a solution to this problem, but after reading a comment by Doug Hughes, I've found a better way to solve this problem, so I'm updating the code. This updated code uses the parent tag as an iterator to loop through the results, which gives Mark the output more to what I think he was after.

Mark Drew posted a question on his blog wondering how you might pass data to a custom tag that acts as a loop. In his post, he's trying to get the following code:

<cfimport prefix="cms" taglib="tags">
<cms:text value="bob">
<cms:loop from="1" to="10">
<cms:text value="bob">
</cms:loop>

To output the following:

bob
I am 1 bob
I am 2 bob
I am 3 bob
I am 4 bob
I am 5 bob
I am 6 bob
I am 7 bob
I am 8 bob
I am 9 bob
I am 10 bob

The solution is to use the parent custom tag as an iterator, which you can do using the <cfexit method="loop"> which will cause the parent tag to start process at the first child tag it finds. This functionality isn't very well documented, and quite frankly, it's something I just learned was possible today.

Here's some source code that shows this off.

<cfparam name="attributes.from" type="numeric" />
<cfparam name="attributes.to" type="numeric" />
<cfparam name="attributes.step" type="numeric" default="1" />

<cfswitch expression="#thisTag.ExecutionMode#">
<cfcase value="start">
        <!---// we're going to start a loop counter to track iterations //--->
        <cfset iLoopCounter = attributes.from />

    </cfcase>

<cfcase value="end">
        <!---// make the next step in the iteration //--->
        <cfset iLoopCounter = iLoopCounter + attributes.step />

        <!---//
            to create a loop, we need to check to see if the current
            loop count is less than the number we're to loop to. if it
            is, we'll invoke the <cfexit method="loop" /> command to
            start process the first child tag.
        //--->

        <cfif iLoopCounter lte attributes.to>
            <!---// start process from the first child tag //--->
            <cfexit method="loop" />
        </cfif>

    </cfcase>
</cfswitch>
<cfparam name="attributes.value" type="string" />

<!---// check to see if we were called from within the loop //--->
<cfif listFindNoCase(getBaseTagList(), "cf_loop") gt 0>
    <!---//
        if inside the loop tag, we need to associate ourselves w/the loop tag.
        this will let the loop tag have access to our variables
    //--->

    <cfassociate baseTag="cf_loop">
    <!---// this is a reference to the parent loop tag //--->
    <cfset oParent = getBaseTagData("cf_loop")>

    <!---// output the value of the current loop and the value of the tag //--->
    I am <cfoutput>#oParent.iLoopCounter#</cfoutput> <cfoutput>#attributes.value#</cfoutput>
    <br />

<!---// ok, we're a standalone tag, so just output the text //--->
<cfelse>
    <cfoutput>#attributes.value#</cfoutput>
    <br />
</cfif>
<cfimport taglib="." prefix="cms" />

<cms:text value="bob">

<cms:loop from="1" to="10">
    <cms:text value="bob">
</cms:loop>

Create all those files in the same directory and run the test.cfm script and you should get the output Drew was hoping to achieve.

Categories: HTML/ColdFusion, Source Code

Comments for this entry have been disabled.