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:
<cfexit method="loop" />
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:
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:
Start Loop<br /> <!--- Loop 10 times using ColdFusion custom tag. ---> <cf_loop10 index="REQUEST.Index"> #REQUEST.Index#<br /> <!--- On the fifth iteration, break out of the loop by destroying the index variable; this will signal to the ColdFusion custom tag that we don't want to iterate again. ---> <cfif (REQUEST.Index EQ 5)> <cfset StructDelete( REQUEST, "Index" ) /> </cfif> </cf_loop10> End Loop<br />
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:
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:
<!--- Check to see which mode we are executing. ---> <cfif (THISTAG.ExecutionMode EQ "Start")> <!--- Param the index attribute. ---> <cfparam name="ATTRIBUTES.Index" type="variablename" /> <!--- Set the loop index. ---> <cfset VARIABLES.Index = 1 /> <!--- Store the current index in the caller scope so that the calling page can access the current loop iteration. ---> <cfset CALLER[ ATTRIBUTES.Index ] = VARIABLES.Index /> <cfelse> <!--- Increment the index. ---> <cfset VARIABLES.Index++ /> <!--- Check to see if we have another loop to do. This will be true if we have not completed all 10 iterations AND our CALLER variable still exists. If the CALLER variable was removed, that will signal the breaking out of the loop. NOTE: We know that this value will exist at least for the first loop iteration. ---> <cfif ( (VARIABLES.Index LTE 10) AND StructKeyExists( CALLER, ATTRIBUTES.Index ) )> <!--- We are still in our 10 loop interations. Store the index in the caller and loop back. ---> <cfset CALLER[ ATTRIBUTES.Index ] = VARIABLES.Index /> <!--- Loop to next iteration. ---> <cfexit method="loop" /> <cfelse> <!--- We have completed all the iterations or been signaled to stop (via the destruction of the index key) so break out of this looping. ---> <cfexit method="exittag" /> </cfif> </cfif>
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.
Want to use code from this post? Check out the license.