I'm working on a website that was coded in ColdFusion. I have a CSS/HTML template I would like to apply to the content of every page, without duplicating any more code than necessary. I've gotten kind of spoiled by ASP.NET's master pages, which would be my preferred way to implement this site. Unfortunately, that option is unavailable to me. This site has to run on Coldfusion MX 7. Also, the developer leading the project doesn't like Fusebox, so that option's out.
The main navigation, graphical header, and footer will be the same on every page. The title tag, meta tags, and level-2 navigation will likely vary from page to page. Aside from that, only the page's "main content area" will be different.
Given these parameters, how can I code the site for maximum maintainability?
Layout is similar to the master page in ASP.NET Web Form. Similar to master page, the Layouts may contain CSS, jQuery files and multiple views. Layout can keep the user interface elements and theme consistency throughout the application. Layouts are the special view type in ASP.NET MVC.
A Master Page is a nonprinting page that you can use as the template for the rest of the pages in your document. Master pages can contain text and graphic elements that will appear on all pages of a publication (i.e. headers, footers, page numbers, etc.)
There are a huge number of ways to do this with ColdFusion.
Application.cfc is executed on every request and has two methods (onRequestStart
and onRequestEnd
) that can be used to prepend/append content to the main script in a page.
Also worth noting, it is possible to extend/inherit your Application.cfc, allowing for a more complex set of RequestStart/End events. More details here and here.
Custom Tags allow you to create a tag that you can wrap around each template to apply the layout/etc. It also allows attributes/etc to define common but changing text.
For example:
<cf_page PageTitle="My Page">
[main page content]
</cf_page>
And inside the custom tag (page.cfm) you have:
<cfif ThisTag.ExecutionMode EQ 'start'>
<cfparam name="Attributes.PageTitle" default=""/>
<cfcontent reset/><cfoutput><!DOCTYPE html>
<html>
<head>
<title>My Website - #Attributes.PageTitle</title>
[styles and scripts and stuff]
</head>
<body>
<div id="heading">
<img src="my_website_logo.png" alt="My Website"/>
</div>
<ul id="mainmenu" class="nav">
[menu]
</ul>
<h1>#Attribute.PageTitle#</h1>
</cfoutput>
<cfelse>
<cfoutput>
<div id="footer">
[footer]
</div>
</body></html></cfoutput>
</cfif>
And of course you can either create multiple custom tags, or one tag which works in multiple ways depending on the Attributes specified.
Henry has already mentioned MVC Frameworks, but you don't need to do MVC to make use of templating/layout functionality.
Fusebox can do MVC, but it doesn't require you to do so, and eitherway FB's ContentVariables are a good tool for implementing modular content with - unless your lead developer can justify his dislike for Fusebox (and suggest an alternative that fits your project better!) then there is absolutely no reason not to go for it - it is a mature and well-known framework, easy to use, plenty of developers, and so on.
However, if Fusebox really is not an option, take a look at Charlie Arehart's list of frameworks - that page in general is a huge list of tools worth looking at.
Anyway, that should give you enough things to consider for now...
ColdFusion developers started using a custom tag called cf_bodycontent in the late 90s to avoid having to include separate header and footer files. That was six or seven years before ASP.NET's Master Pages. ;-)
Now there's a native tag that does the same thing: cfsavecontent. Here's the essence of how people use cfsavecontent in templates.
<!--- index.cfm --->
<cfsavecontent variable="content">
<cfinclude template="#url.action#.cfm">
</cfsavecontent>
<cfinclude template="template.cfm">
<!--- template.cfm --->
<cfparam name="title" default="Welcome">
<html>
<head><cfoutput>#title#</cfoutput></head>
<body>
... header, menu, sidebar, whatever ...
<cfoutput>#content#</cfoutput>
... right column, footer ...
</body>
</html>
<!--- foo.cfm --->
<cfset title="Welcome to Foo">
Hello World! I'm the page at index.cfm?action=foo
<!--- bar.cfm --->
<cfset title="Welcome to Bar">
Hello World! I'm the page at index.cfm?action=bar
If you want put a template within a template, just add another cfsavecontent.
<!--- index.cfm --->
<cfsavecontent variable="content">
<cfinclude template="#url.action#.cfm">
</cfsavecontent>
<cfsavecontent variable="content">
<cfinclude template="internal_template.cfm">
</cfsavecontent>
<cfsavecontent variable="content">
<cfinclude template="master_template.cfm">
</cfsavecontent>
<cfoutput>#content#</cfoutput>
You could refactor to cut out the redundancy.
<!--- index.cfm --->
<cfsavecontent variable="content">
<cfinclude template="#url.action#.cfm">
</cfsavecontent>
<cfparam name="templates" default="internal,master">
<cfloop list="#templates#" index="t">
<cfsavecontent variable="content">
<cfinclude template="#t#_template.cfm">
</cfsavecontent>
</cfloop>
<cfoutput>#content#</cfoutput>
If you want to have one template "extend" another, you could maybe do so by turning the list into a stack, and having each template push its parent onto the stack.
<!--- internal_template.cfm --->
<cfset templates = listAppend("master", templates)>
...
<cfoutput>#content#</cfoutput>
...
<!--- index.cfm --->
<cfsavecontent variable="content">
<cfinclude template="#url.action#.cfm">
</cfsavecontent>
<cfparam name="templates" default="internal">
<cfloop condition="listlen(templates) gt 0">
<cfset t = listFirst(templates)>
<cfset templates = listRest(templates)>
<cfsavecontent variable="content">
<cfinclude template="#t#_template.cfm">
</cfsavecontent>
</cfloop>
<cfoutput>#content#</cfoutput>
And thus you have StackBox, a ColdFusion framework invented on StackOverflow. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With