A Case Against ColdFusion Custom Tag Page Wrappers
Posted January 4, 2007 at 7:39 AM by Ben Nadel
I have never used ColdFusion custom tags to wrap around the content of a page as in this example:
- <!--- Wrap page output. --->
- Welcome to this page
- Doesn't she rock!?!
For my Team Nylon project, I thought I would give it a go; see what all the hubbub is about. Things started out nicely but then I hit a HUGE bump. I was looping over a large collection of data output tables and putting a CFFlush tag after each table to flush it to the browser when I got this error:
Unable to perform CFFLUSH.
You have called CFFLUSH in an invalid location, such as inside a CFQUERY or between a CFML custom tag start and end tag.
YIKES! I forgot you cannot perform a CFFlush inside a custom tag. Well, so much for that idea. Not being able to perform a CFFlush violates one of my two laws on proper page design and rendering. Looks like it's back to just including the header and footer templates.
I wonder, if you cannot perform a CFFlush during a custom tag's execution, does ColdFusion every flush content to the browser when processing a custom tag? If this is the case, then that means that the use of a ColdFusion custom tag page wrapper delays content flushing until the entire page has been processed. That get's my panties in a bunch!
What Other People Are Searching For
I'm a big fan of custom tag wrappers. Since I never use cfflush I guess I never thought about this issue. I guess my feeling is that if you have that much data to render on a page you should probably use paging to limit the results - so I never saw the need for cfflush. </mytwocents>
CFFlush, in most cases is not required, even in places that I use it. I like it because I get a perceived speed increase in page loading time. Now, this speed increase may be fractions of a second, but I find that this makes a difference.
For those of you who think fractions of a second don't matter when it comes to perception, think about watching a movie where the sound is not synced up with the video. If that sound is even fractions of a second off, how does it make you feel? Uncomfortable right? You can clearly tell something is wrong with the movie. Can you even concentrate on the movie at this point? I can't (but maybe I am crazy).
Now, move that to web applications; a user will "feel" the difference in small lags in page load. CFFlush goes that little extra bit further to try an alleviate this lag time in what the user "feels" even if the overall page load time is not different (and from what people tell me, sometimes actually longer).
why not just use something like Model-Glue which has support for page "templates". That way you can display whatever content wrapped in whatever without using include files.
There is something about Model-Glue that makes me feel uncomfortable. I am not a big fan of huge, up-front configuration and building page templates "after the fact". But, I am still working all this out in my head... nothing I have done so far is the "right" way. Who knows, I might end up with Model-Glue in the end.
After having used MachII and Model Glue rather extensively, I can say that the switch in development mindset is worth it.
I don't know if I would say that MachII or Model Glue requires Big Upfront configuration. Normally, once the framework files have been dropped into the webroot or mapped properly, you can begin coding.
Most developers have built mini-frameworks to manage their development. The advantage of the standard frameworks, in my opinion, is to have a standard functionality used amongst many applications. This reduces future maintanence, my least favorite spot in the application life cycle.
Your Mileage may vary.
Certainly I am excited about Model-Glue. I keep trying to get into it, because there is a bunch of stuff that looks cool. I am sure one day I will get it.
As far as the upfront configuration, I am talking about the potentially massive XML config files that wire up all the events, handlers, views, results. I am not a fan of centralized configuration; something seems wrong about it (in my gut). But then again, like I said, nothing that I HAVE DONE feels all that right either.
Maybe I just need to fail a ton of times before I can succeed.
I didn't quite mean to start a framework/no-framework discussion. I've used Model-Glue and although it has it's shortcomings, with good planning it's very nice. I am not sure I would build anything new in ColdFusion without it. That being said, I realize that the xml config is somewhat of a pain and I'm thinking of ways to make that easier to work with. Some way of maybe editing the xml files through CF so you don't have to dig through tons of xml when you need a small change. I don't think however that the "templates" are an after-thought but it depends on your approach. There has to be some planning involved and there is a learning curve.
Ben, rather than having a <cfflush> inside your loop (and inside your custom tag, which is a non-starter), simply put a <cfflush interval="#someAppropriateValue#"> BEFORE the custom tag.
That said, CF probably holds onto the generatedContent of the custom tag until the tag is closed, so all that stuff will still come down in one big wallop @ the end. You can, however, at least get some of the response back to the browser from the outset.
Not a solution to all situations, but handy to bear in mind.
I'm right there with Ben that not being able to use cfflush is a deal-killer, personally.
I have a project that requires a tree menu with over 100,000 hierarchical items, broken into three sections. Even compressed, the generated HTML is over 1.2 megs.
The perceived performance increase that cfflush provides is invaluable. In my case it means the difference between the user having to wait 1 second and 10 seconds... I'd say that alone is a pretty substantial argument.
CF Custom Tags are nice, I wouldn't dismiss them so fast. But they DEFINITEY pose a problem when wrapping a LOT of content. I stick to using them when the content it wraps isn't too huge, and I know the content the tag will wrap won't get huge.
I ran into a problem like you had, but my problem was slightly different. It was a CF5 app, and my custom version of "cfsavecontent" tag (cfsavecontent wasn't available until CF6) wrapped a huge result set and I was getting "Server is busy and can't handle requests, please try again later" errors. It turned out that the custom tag could only handle so much output between it's bounds before throwing a weird error.
If I was able to CFFLUSH within my loop, I'm sure that would have solved the problem, but I suspect macromedia disallows CFFLUSH within custom tags because they don't know if you're planning on saving the content between the tags and NOT displaying it to the browser (maybe you're emailing it and it will never make it to the browser).
Anyhow, I make sure that I ONLY use custom tags that wrap content now if the content they wrap is guaranteed to stay at a reasonable size.
One other thing I highly suggest doing is ALWAYS specify an end tag for a custom tag/module call. If you're using CFMODULE, you don't HAVE to specify an end tag and that can get you into trouble if you have neseted CFMODULE calls. Specifying an end tag of course will cause the tag to execute twice, so you need to add code to "ignore" the second tag call (<cfif NOT compareNoCase(thisTag.executionMode, "end") ><cfexit></cfif> )
"I have a project that requires a tree menu with over 100,000 hierarchical items, broken into three sections. Even compressed, the generated HTML is over 1.2 megs."
The web is not where you need to be my friend.
"The web is not where you need to be my friend."
LOL, I've actually been here a while and do quite well.
It's an intranet app on a high-end network, so the bandwidth isn't a huge problem. AJAX is out of the question for accessibility reasons. I'm always open to suggestions though, if you have something better in mind.
First off, I am not dismissing custom tags. I use them a lot and they really whip the llama's ass (winamp reference). However, I think you are on the right track - not using custom tags for wrapping large chunks of data. That makes me very comfortable.
You may want to look at using OnRequestEnd.cfm or the OnRequestEnd listener method if you use Application.cfc. I've been using this for some time to wrap themes around content without using custom tags.
Typically I'll do something like:
<!--- Application.cfc --->
<cfparam name="request.node" default="dsp_home.cfm">
<!--- site_style.cfm --->
<head and stuff></...>
Formatting, header, navigation, etc....
<!--- dsp_home --->
Page content stuff here.
This allows cfflush to operate while still giving you the template wrapping effect. For me it gives a very easy way to skin based on client settings or even between applications and subsystems.
I think I see what you are doing, but I am not sure I see a real difference between including a header and footer elsewhere (other than this is one place vs. many places).
If you have enabled compression, you can't really flush a page. And if your pages are THAT big, why HAVEN'T you enabled compression?
I don't know anything about compression. However, I doubt that I will ever have an application where every page is so big that such extreme compression is required.
I don't always CFFlush for a load time requirement; sometimes, I call a CFFlush as a matter of "best practice". Whenever my page reaches a horizontal clean break, I like to throw a little flush in there. For example, after the "header" has rendered or before the "footer" starts. There is no need to wait for the whole page to load before sending those to the client.