Yesterday, I was messing with ColdFusion custom tags when I wondered if there was any way to break out of a loop created using ColdFusion custom tags and the CFExit tag:
Launch code in new window » Download code as text file »
After a few minutes of coding, I discovered that the CFBreak tag throws an error and that all of the CFExit variations, when used in the calling page, cause processing to completely stop in the current template (not the custom tag loop).
Ok, so clearly, there was no inherent way to break out of a ColdFusion custom tag loop; so, what would be the cleanest way to create such functionality. I considered adding some sort of "continue" condition attribute that could be added to the tag. I also considered creating a child tag that could signal the break to the parent tag:
Launch code in new window » Download code as text file »
But, both of these methodologies required me to add more stuff to the tag. I wanted to try and do this without any extras. That's when it occurred to me - what if we could signal the ending of the loop by destroying the index variable. That's when I came up with this:
Launch code in new window » Download code as text file »
As you can see above, on the fifth iteration of the loop, we are deleting the Index variable from the REQUEST scope. This should signal to the ColdFusion custom tag that the loop is ending. And, in fact, when we run the above code, we get the following output:
Start Loop
1
2
3
4
5
End Loop
It works, but of course, it doesn't work without a little elbow grease; we have to update the ColdFusion custom tag loop to abide by this index-deletion agreement:
Launch code in new window » Download code as text file »
This works, but is definitely a bit of a sloppy hack. For starters, because our stop-logic is in the End tag logic of the ColdFusion custom tag, we know that the custom tag loop will execute at least once, no matter what. Furthermore, because the "break" here depends on the existence of a variable at the time of end-tag execution, its not a hard break; this means that the even if you destroy the index variable at the beginning of the loop logic, the rest of the code within the loop will execute.
So, it's not perfect, but I thought I would throw it out there in case it helped anyone.
Download Code Snippet ZIP File
Comments (6) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
Using StructKeyExists() With CALLER Scope In ColdFusion Custom Tags
Fundamentals Of Object-Oriented Design In UML By Meilir Page-Jones
I think I may be missing something. If you want to stop looping, why not just not output the cfexit/loop tag? That signals CF to repeat the custom tag, so just don't output it. Again though I think I'm missing something.
Posted by Raymond Camden on Sep 23, 2008 at 10:44 AM
@Ray,
That's basically what I am doing. From within the custom tag, if the "index" variable does not exist in the CALLER scope, I am not using the CFExit/Loop tag, but rather the CFExit/ExitTag tag.
What I was trying to do was to find a way for the calling page to "signal" to the custom tag to perform this logical choice.
Posted by Ben Nadel on Sep 23, 2008 at 10:56 AM
Ohhhhh ok. That makes sense.
Posted by Raymond Camden on Sep 23, 2008 at 12:09 PM
@Ray,
I'll probably never use something like this, but I figure it's good to have in the bag.
Posted by Ben Nadel on Sep 23, 2008 at 12:45 PM
Hi Ben.
This looked like a sitter for a nested tag, and getting the nested tag to communicate with the parent tag via <cfassociate>. As I had never used <cfassociate> before, I decided it was a good opportunity to investigate further.
Here's what 15-odd min of investigation lead me to:
<!--- caller.cfm --->
<cf_loop iterations="10" index="i">
<cfoutput>[#i#]</cfoutput>Hello World<br />
<cfif i gt 5>
<cf_break>
</cfif>
</cf_loop>
<!--- loop.cfm; based on your one--->
<cfif structKeyExists(thistag, "breakAttributes") and thistag.breakAttributes[1].break>
<cfset thistag.generatedContent = "">
<cfexit method="exittag">
</cfif>
<cfif (THISTAG.ExecutionMode EQ "Start")>
<cfparam name="attributes.index" type="variablename">
<cfparam name="attributes.iterations" type="integer">
<cfset variables.index = 1>
<cfset caller[attributes.index] = variables.index>
<cfelse>
<cfset variables.index = variables.index + 1>
<cfif variables.index lte attributes.iterations>
<cfset caller[attributes.index] = variables.index>
<cfexit method="loop">
<cfelse>
<cfexit method="exittag">
</cfif>
</cfif>
<!--- break.cfm --->
<cfset attributes.break = true>
<cfassociate basetag="cf_loop" datacollection="breakAttributes">
Sorry if I'm putting the associate logic in the wrong place, like I said, this is the first time I've had call to use the tag, so am just trying follow livedocs, and make some sense out of what they say (the examples are pitiful).
--
Adam
Posted by Adam Cameron on Sep 23, 2008 at 12:46 PM
@Adam,
That looks pretty good. Nice proof of concept. As far as the CFAssociate tag, I am not sure if it matters where it goes exactly; I tend to put it as one of the first things in the tag.
If you are looking into nested tags, definitely take a look at the GetBaseTagData( "PARENT_TAG_NAME" ) function. This function allows the child tag to "reach" into the data of the parent tag. Its sort of a reverse relationship to the CFAssociate tag, but one that I have found to be more useful.
Posted by Ben Nadel on Sep 23, 2008 at 1:23 PM