Question

I have a small coldfusion section of our site that all uses similar js and css files and page structure. The code is currently repeated for each file, and I'd like to factor it out and set something up using a master page and templates.

Master.cfm page:

<!--- Master template, includes all necessary js and css files. 
    Expects following variables to be defined:
    - pageName - name of the file to be loaded as the body of the page 
    - title - text to be used as the title of the page and as the header text in the header bar --->
<cfinclude template="_lockedPage.cfm" />

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>#title#</title>
        ... all script and css links here ...
        <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
        <script type="text/javascript" src="js/jquery.mobile-1.3.2.js"></script>
        ... etc ...
    </head>
    <body>
        <div data-role="page">
            <div class="headerDiv" data-role="header" data-theme="b" data-position="fixed">
                <a id="backButton" data-role="button" data-direction="reverse" data-rel="back" data-icon="arrow-l" data-iconpos="left" data-theme="a">Back</a>
                <h1><cfoutput>#title#</cfoutput></h1>
                <a href="index.cfm" data-role="button" data-icon="home" data-iconpos="left" data-theme="a">Home</a>
            </div>
            <div data-role="content" class="container">
                <cfinclude template="#pageName#.cfm" />
            </div>
        </div>
    </body>
</html>

Then a page example would be something like this. CustomerSearch.cfm:

<cfscript>
    title = "Customer Search";
    pageName = "_customer-search";
    include "Master.cfm";
</cfscript>

And then I would need a _customer-search.cfm page that would include all the body content for the page.

This means that I would need 2 files for every page that we currently have - the outer page that defines the variable and includes the master page, and the template page that has the individual page content.

Is this a good logical structure? Is there anyway to improve it?

Was it helpful?

Solution

You have the right idea, but I think you'll end up with a lot of unnecessary files. You could instead create a header.cfm and a footer.cfm that contain your global HTML. Each page would include those files and the content would be written between.

<cfset title = "Customer Search">
<cfinclude template="global_header.cfm">

<!--- This will be the content of your page. --->

<cfinclude template="global_footer.cfm">

This file would be named customer_search.cfm. Anytime you update the header or footer, it's a global change.

If you have a lot of business logic and query code that needs to exist on multiple pages, you might look into using an MVC framework to help you organize and reuse code. I prefer ColdBox (try ColdBox Lite), but many people use Framework/1.

OTHER TIPS

I found that using custom tags was another simple solution and in my opinion a better solution to creating a separate header.cfm and footer.cfm.

In master.cfm:

<cfif ThisTag.ExecutionMode EQ 'start'>
  [HEADER]
<cfelse>
  [FOOTER]
<cfif>

In each content page:

<cf_master>
  [CONTENT GOES HERE]
</cf_master>

If you'd like to pass in variables to the master page simply add it as an attribute to the opening tag:

<cf_master Title="Content Title">

And make sure the attribute is specified in the master file:

<cfparam name="Attributes.Title" default=""/>
<head>
  <title><cfoutput>#Attributes.Title#</cfoutput></title>
</head>

The key for me was understanding the ThisTag.ExectuionMode. If you use custom tags you can either use just one tag or use an opening and closing tag. If you use an opening and closing tag then you can choose to include some content in the opening tag <cf_master>, and other content in the closing tag </cf_master>. That is why you need the if/else condition in master.cfm. In this case it is useful because then you can include a HEADER in the opening tag and a FOOTER in the closing tag.

Also, in case this isn't obvious, when you call your custom tag, it should match the name of the file where the code is stored. In my case <cf_master> matches master.cfm.

I used this page as a tutorial for custom tags: https://www.petefreitag.com/item/64.cfm

The Application.cfc can be a great use for common page design. Basically have one template and inject the pages generated content. Dan Bracuk commented in the other solution about using the Application.cfc onRequestStart() and onRequestEnd() methods but I use it slightly differently. Here is my general setup:

Application.cfc

// This is <cfscript> but it could be regular CFML too
component {
    public function onRequest( required string targetPage ) {

        // Capture/buffer the requested pages output
        savecontent variable='LOCAL.output' {
            include ARGUMENTS.targetPage;
        }


        // Use the output as the page content
        // if the page did not specify content
        param string REQUEST.content = LOCAL.output;


        // Inject the design template
        // which should output the page content somewhere
        include '/path/to/template.cfm';
    }
}

template.cfm

<!DOCTYPE html>
<cfparam name="REQUEST.title"   type="string" /><!--- required --->
<cfparam name="REQUEST.head"    type="string" default="" />
<cfparam name="REQUEST.content" type="string" /><!--- required --->
<html>
    <head>
        <title><cfoutput>#REQUEST.title#</cfoutput></title>
        <link rel="stylesheet" href="path/to/common.css" />
        <script src="path/to/common.js"></script>
        <cfoutput>#REQUEST.head#</cfoutput>
    </head>
    <body>
        <header>...</header>
        <cfoutput>#REQUEST.content#</cfoutput>
        <footer>...</footer>
    </body>
</html>

each-page.cfm

<cfset REQUEST.title = "My Page Title" />


<cfsavecontent variable="REQUEST.head">
    <!-- page specific head elements here -->
</cfsavecontent>


<!-- Regular page code/HTML output here -->
<!--- or you could use another <cfsavecontent> block --->
<!--- to save specific output sections --->
<p>Hello World</p>

This way allows you to keep the template all within one file which is much easier when designing it in a WYSIWYG manor. It also allows each page to set variables used in the design template, since the requested page is executed before the design template is included.

And, there is no need to <cfinclude> templates on each page since Application.cfc onRequest() will get called for ALL pages by default. If there are .cfm pages which should NOT include the design template, such as PDF output, then you'll need to add some logic to just dump the output and not include the design template.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top