Dynamic Templating with ColdFusion
Jul/091
“Hey, what’s this? ColdFusion is using dynamic templates, didn’t you ever come across one of them .cfm-files?” Ah, but this is not what I’m talking about here. I’m talking about clean HTML-snippets with some placeholders in them, so you, the programmer, can hand the design task to somebody who finds beauty someplace else than in a brilliant piece of code. These people are called web designers and while they can do all sorts of magic with HTML, CSS and Photoshop, they get tend to be irritated by long blocks of cfscript, queries and other business logic in their template code.
I’m a server side coder – I don’t make pretty. (Raymond Camden)
So this is about separating this business logic from the real HTML template.
This concept is far from new, there is the much adored MVC pattern dictating just this approach. If you’re working with a CF-framework such as Fusebox, Mach-II or Model-Glue, you’ll be most familiar with the concept. But sometimes (IMO more often than not) these frameworks actually suck up too much performance or really get in your way, sometimes you have to deal with legacy applications where it’s just not feasible to rewrite your code for such a framework, though I strongly suggest you try a couple of them and even you decide you didn’t exactly fall in love, you’ll learn from the concepts used there.
Anyway, now you’re in the position to roll your own templating engine, you’ll ask yourself how to begin this task. We originally started with an ugly series of ReReplaces, which really sucked performance-wise. Here’s some example that might give you some idea on an alternative approach, using evaluate() and the lesser known DE()-function.
It is a general notion that evaluate() should be avoided at all cost. This is because it is causing additional processing overhead and it often introduces security risks. In our case, we’re trading a couple of complicated and error-prone ReReplace-passes with one replace and one evaluate, so the processing issue shouldn’t hit us that bad. The security issue remains however – you either need to be able to really, really trust your webdesigner (and never, ever let your average l33t h4xx0r user define such templates!) or you’ll need to implement some additional security measures to check the templates for malicious code. Let me assure you, you don’t want to go down this last path, it will eventually, yet almost inevitably lead to failure.
Okay, long preamble again, on to the code – this will spare you further explanations:
Here is some template snippet we’ll want to process:
<html> <head> <title>Hello [myVar] Demo</title> </head> <body> <b>Hello [myVar]</b><br /><br /> Time is [DATEFORMAT(now(),'HH:mm')] now. <br /><br /> The string '[myVar]' has [len(myVar)] characters. </body> </html>
Now we’ll process this:
<cfscript> // set the variables for the tokens used in the template: myVar='World'; // first we'll replace the brackets with hash characters variables.myTemplate=ReReplace(variables.myTemplate,'[\[\]]',chr(35),'ALL') // now we'll process the expressions in the brackets variables.myTemplate=evaluate(DE(variables.myTemplate)); // and output the result writeOutput(variables.myTemplate); </cfscript>
This should render the following source:
<html> <head> <title>Hello World Demo</title> </head> <body> <b>Hello World</b><br /><br /> Time is 12:04 now. <br /><br /> The string 'World' has 5 characters. </body> </html>
Quick and painless. You can use the same approach for other use cases like dynamic queries. It is definitely slower than static templates (i.e. Hello #myVar#), but still faster than parsing a string in several passes for different substrings you want to replace.