Ask a Jedi: Handling print format when using custom tags

This post is more than 2 years old.

Devon asks:

I am using custom tags to do my layout and thought this would be great for creating printable pages easily by being able to just include a different stylesheet if boolean print is true. Then I thought after reading the reporting chapter in WACK 1 why not cfdocument ...well because it moans about unclosed tags and I kinda saw that coming once I opened my 1st if statement.

So I'll sit this evening and give Ch 25 from WACK 2 a proper read but I was wondering if it would be possible to print out the content of a custom layout tag using cfdocument and maybe a content variable?

So first off - let's make sure we understand the issue Devon found. You cannot open a cfdocument block inside a cfif. What he tried was probably something like this:

layout.cfm <cfif thisTag.executionMode is "start"> <cfif structKeyExists(url, "print")> <cfdocument format="pdf"> </cfif> <cfelse> <cfif structKeyExists(url, "print")> </cfdocument> </cfif> </cfif>

If you try to run this, you will get an immediate error. So what to do?

One option is to not worry about changing the custom tag. This is a perfect example of when onRequest would be useful. The onRequest method of Application.cfc would run after the entire request is done, and before anything is sent to the user. You could easily when wrap the output in cfdocument tags. But don't forget that onRequest has the bad side effect of breaking flash remoting/web service calls unless you hack around it.

You can get it to work inside a custom tag - but it feels a bit hackish to me. Here is an example. First, let's start with a super simple layout custom tag:

layout.cfm <cfif thisTag.executionMode is "start"> <h2>Header</h2> <cfelse> <p> Footer </p> </cfif>

And now let's look at a modified version:

layout_withprint.cfm <cfif thisTag.executionMode is "start"> <cfsavecontent variable="header"> <h2>Header</h2> </cfsavecontent> <cfelse> <cfset content = thisTag.generatedContent> <cfset thisTag.generatedContent = "">

&lt;cfsavecontent variable="footer"&gt;

&lt;cfif not structKeyExists(url, "print")&gt;
	&lt;cfheader name="Content-Disposition" value="inline; filename=print.pdf"&gt;
	&lt;cfdocument format="pdf"&gt;


So what did I change? First look at the top half of the code. Notice I no longer output the header. Instead I save it to a variable. In the next part of the custom tag - I first take everything that happened between the first and ending tags, which exists in thisTag.generatedContent, and save it to a variable. I then erase the thisTag value. This will essentially save and remove any output that occurred. I then create a variable for the footer. Lastly, I do one of two things. I check to see if a url.print variable exists. If it doesn't, I simply output my values. Note the header, which was made in the beginning of the custom tag, still exists, even though technically I'm in the second call of the tag now. If url.print does exist, I output the same thing but wrap it with cfdocument tags.

I think perhaps this could be a bit nicer, but hopefully it gives you some ideas.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA

Archived Comments

Comment 1 by todd sharp posted on 12/23/2007 at 5:18 AM

I have a similar solution that I posted a while back.

Looking back it's not the most elegant solution either. Interesting idea about using onRequest - I hadn't thought of that before but I may consider that in the future.

Comment 2 by Dan G. Switzer, II posted on 12/24/2007 at 6:55 PM

I used to split my page layout tag (which generates the footer/header around content) to have the "start" mode do the header and the "end" mode do the footer.

However, I quickly learned that it was tough to read and would often create situations I couldn't workaround easily--like you descrip.

So, I use a similar technique, but I think it's even simpilier--I just place all my wrapper code in the "end" execution mode:

<cfif thisTag.executionMode is "start">
<!---// define any attributes here //--->

<!---// define any global variables here //--->
<cfset content = thisTag.generatedContent>
<cfset thisTag.generatedContent = "">



The thing I like about this is it keeps all my HTML together in a way that's very easy to read.

I use the "start" block to initialize all my variables and do any of the code centric work.

I find this method to work much more effectively for me.

Comment 3 by Devon Burriss posted on 12/24/2007 at 7:57 PM

Thanks Ray
Much appreciated!