Russian Doll Content Wrapping With CFSaveContent In ColdFusion
In web development, the term "Russian Doll" is sometimes used to refer to content that is wrapped inside another piece of content of the same type. This is based on the Russian Doll toy (Matryoshka), which has a multitude of smaller toys contained within it. In the past, I've looked at using the Russian Doll pattern for error handling in Node.js as well as for error handling in ColdFusion. But, its value extends beyond just errors - I often use the CFSaveContent
tag to build up a content payload from the outside in. And, I thought it would make for a nice example.
If you dig into web application frameworks like Framework One (FW/1), they often use Russian Doll content nesting to render page responses. They do this by storing content into a given variable; and then, redefine said variable, over and over again, referring to itself in the subsequent expressions.
This might sound strange in the abstract, but we actually use this kind of approach all the time. Consider incrementing a numeric value:
value = ( value + 1 );
In this assignment, the value
variable is being redefined using itself as part of the evaluated expression. When it comes to wrapping content, it's the same exact thing. And, with ColdFusion, the CFSaveContent
tag makes this Russian Doll nesting extremely easy!
In the following ColdFusion template, I'm going to use several successive CFSaveContent
tags. Each tag is going to assign content to the body
variable; and, all but the first tag are going to include the body
variable within their own tag bodies:
<cfoutput>
<!--- Define the initial BODY content. --->
<cfsavecontent variable="body">
<p>
This is the body!
</p>
</cfsavecontent>
<!--- Wrap the BODY content in more content. --->
<cfsavecontent variable="body">
<header>
This is the header.
</header>
<main>
#body#
</main>
<footer>
This is the footer.
</footer>
</cfsavecontent>
<!--- Wrap the BODY content in EVEN MAOR content! --->
<cfsavecontent variable="body">
<!doctype html>
<html lang="en">
<head>
<title>
ColdFusion is Amaze-balls!
</title>
</head>
<body>
#body#
</body>
</html>
</cfsavecontent>
<!--- Output the doubly-wrapped content. --->
#body#
</cfoutput>
As you can see, each subsequent CFSaveContent
tag wraps the previous tag's body
value by interpolating the #body#
string into its own tag body evaluation. Each body
value is "Russian Doll nested" back into itself.
If we then run this ColdFusion page and look at the generated source, we get the following output - I've decorated each branch of the DOM (Document Object Model) tree that was supplied by a CFSaveContent
tag:
The CFSaveContent
tag is definitely one of the unsung heroes of the CFML language. I use it all the time to store generated content into a variable (this is especially helpful in ColdFusion custom tags). And, when composing layouts, the CFSaveConent
tag combined with Russian Doll nesting is a force to recon with!
Want to use code from this post? Check out the license.
Reader Comments
I love cfsavecontent. Use it all the time for building emails or csv files. Especially when the output is space sensitive
@Tom,
💯 And, if you're using Lucee, I keep forgetting that they have a few extra attributes on the
CFSaveContent
tag -trim
andappend
. So nice!@Ben,
I love this (hidden from me) gem! I really hadn't ever considered using it in this way, but brilliant 👌 wunderbar 🎉 excellent 🙌
simple, elegant AND OH SO useful!
@Chris,
Awesome! That's exactly what I like to over-share -- you never know what someone has or has not seen yet. Never hurts!
@Ben,
Indeed. Much appreciated. Everything we've ever learned we've once learned for the first time. Today is that day for this particular topic 😂
Cool example! I've only recently started using cfsavecontent to address a problem with buffer over-run, so I'm wondering if there are any downsides I'm not seeing. I've got an application that has one page that can (in very limited circumstances) be very large and trigger the "Unable to add text to HTML HEAD tag." error. As I'm on a shared server, I can't change the maximum buffer size, so I put the whole page into cfsavecontent and problem fixed. The page contains a form and actions, but as far as I can tell, still functions normally. Do you know if there anything I should be looking out for?
@Stuart,
I'm not familiar with the buffer over-run problem that you're speaking of. I know that you can configure (in the CF Admin) the maximum size of the request; which is maybe what you're talking about (but can't change on shared hosting). That said, I have on idea why putting things into a
cfsavecontent
would have any impact on that since you're still sending the same amount of data over the request in the end.Very curious! That said, I can't think of any significant downside. The only possible downside that I can think of is that using
cfsavecontent
limits your ability to "stream" data back to the browser. What I mean by that is that if you entirely build-up the page on the server first, you can't start sending those bytes down the browser until you've already fetched all the data. However, in theory, if you built your HTML head first, then flushed it, then built up the page content, you could be more optimal. Of course, that approach also has lots of problems, like what happens if you flushed the head and then need to do a redirect -- you can't.All to say, I wouldn't have any concern about using
cfsavecontent
to construct your page. That's what I'm doing and I don't have any problems.@Ben
Thanks so much for the quick reply, and sorry it took so long for mine (not sure if I should have, but I didn't get an email saying you replied)! Yeah, this is the first time I've come across this buffer issue, and I had the same thought as to why this should work when the amount of data should be the same. Either way, it seems to address things without side effects, and I feel better reading your reply. Thanks again!
@Stuart,
Yeaahhhh, sorry about that. Over the weekend I realized that I actually broke comment emails for the last year 😱 😱 😱 Very embarrassing. But, I'm hoping that you get this email notification!
Got it 😊
Woot! 🎉 🎊 💥 💯 Thank you for confirming. I can't believe this has been broken since like last February (2023).
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →