Expected And Unexpected getBaseTagData() Behavior In Lucee CFML 5.3.7.47
In the implementation details of my ColdFusion custom tag DSL for HTML emails, I have to access the data exposed by ancestor custom tags. In some cases, the parent tag is dynamic; which means that I have to use the getBaseTagList()
function in order to figure out the name of the ColdFusion custom tag that I need to access. It turns out that some native ColdFusion tags show up in the getBaseTagList()
value; but, they do not expose any "data". As such, they have to be explicitly skipped-over. Things get even more complicated when you use CFModule
to invoke a custom tag. And, since I stumbled over this in my journey, I figured it might be worth a quick demo in Lucee CFML 5.3.7.47.
One way in which a ColdFusion custom tag can communicate with another ColdFusion custom tag is by using the getBaseTagData()
. This allows one tag to reach up into the variables
scope of another tag to read-from or write-to various data structures. But, calling getBaseTagData()
doesn't always work, depending on the type of tag and the way in which the tag was invoked.
To see this in action, I have a test page that nests some native ColdFusion tags and some custom tags. Notice that one custom tag in particular - Wrapper.cfm
- is in there twice: once using the CF_
notation and once using CFModule
:
<cfsavecontent variable="content">
<cf_Wrapper>
<cftimer type="outline">
<cfmodule template="Wrapper.cfm">
<cf_MyTag />
</cfmodule>
</cftimer>
</cf_Wrapper>
</cfsavecontent>
<cfset echo( content ) />
The cf_MyTag
tag is our ColdFusion custom tag that is going to try and walk up the tree of tags and access the data attribute. Each tag will be accessed in a try/catch
so that we can see which worked and which didn't:
<cfscript>
dump( label = "Base Tag List", var = getBaseTagList() );
// Loop over each item in the base-tag list and try to access the "Data".
// --
// CAUTION: This does NOT WORK. Native ColdFusion tags show up in this list but they
// DO NOT expose tag data the way that custom tags do.
loop
item = "parentTagName"
list = getBaseTagList()
{
echo( "<br />" );
try {
dump( label = parentTagName, var = getBaseTagData( parentTagName ) );
} catch ( any error ) {
dump( "Oh Noes! getBaseTagData( #parentTagName# ) failed!" );
}
}
exit method = "exitTag";
</cfscript>
As you can see, there's nothing special going on here - we're just walking the list of ancestor tags and trying to access the data. And, when we run this ColdFusion code in Lucee CFML, we get the following output:
Ok, so the first take-away here is that some native ColdFusion tags show up in the getBaseTagList()
. If I had to guess, I'd say that it's any tag that is concerned with "wrapping content". For example, while I didn't test it, I would guess that the CFXML
tag also shows up in this list since it wraps and parses content.
These native ColdFusion tags should be skipped when dynamically accessing a parent custom tag. In fact, I assume that the only tags that can be accessed are those with the CF_
prefix. Which brings us to the second and arguably much more important lesson: ColdFusion custom tags do not expose data when invoked using CFModule
(in Lucee CFML).
If you refer back to test.cfm
above, you'll see that the Wrapper.cfm
template is in the demo twice:
<cf_Wrapper>
<cfmodule template="Wrapper.cfm">
And, while this is the same exact tag, getBaseTagData()
only works when the tag was invoked using the CF_
prefix, not when using the CFModule
tag. This may be related to an earlier issue I posted in which I demonstrated that getBaseTagList()
and the CFModule
tag works different in Adobe ColdFusion vs. Lucee CFML.
ASIDE: ColdFusion custom tags will also expose data when invoked using the
CFImport
tag. This is the basis of how my ColdFusion custom tag DSL for HTML emails works - it'sCFImport
tags all the way down!
Now, if we run similar code in Adobe ColdFusion 2018 (the syntax had to be modified), we get the following output:
As you can see, with Adobe ColdFusion 2018, the Wrapper.cfm
exposes data regardless of the way in which it was invoked (CF_
prefix of CFModule
tag). However, when we look at the output of the getBaseTagList()
, we see a potentially key difference:
Lucee CFML:
CF_MYTAG, CFMODULE, CFTIMER, CF_WRAPPER, CFSAVECONTENT
ACF 2018:
CFDUMP, CF_MYTAGACF, CF_WRAPPER, CFTIMER, CF_WRAPPER, CFSAVECONTENT
In Adobe ColdFusion, the Wrapper.cfm
template shows up as CF_WRAPPER
regardless of the invocation approach. This speaks to my earlier post about the difference in engines; and, it may be the underlying root cause of the problem. Perhaps Lucee CFML won't even check for base-tag data unless the invocation "expression" starts with CF_
?
I'll add this finding as an additional note in the compatibility bug that I filed on the Lucee JIRA server.
Want to use code from this post? Check out the license.
Reader Comments
Hi Ben
Is this a problem only with Lucee version 5.3.7.47 ? and if so is there a workaround?
Thanks,
Mauro
@Mauro,
Hmm, good question. I honestly don't remember which part of my email DSL caused this problem. Because, I certainly do use
CFModule
extensively inside the DSL implementation, in both Lucee and Adobe ColdFusion.I just took a look at the code, and it seems that I have some places where I call
.filter()
on the list (array) of tag names and then only include ones that match the given Regular expression:^cf_
So, it seems that I only include tags that have the "cf_" prefix, which is when the
getBaseTagData()
call actually works. It's not a great work-around; but, it worked OK in my particular case. It then just becomes a "known limitation" for the DSL I'm using.Thanks for your prompt response Ben and may I say its a privilege :-)
Is it an official workaround or a hack that you have tried and tested? Also is applying this in terms need consideration regarding case sensitivity?
Kind regards,
Mauro