logo image

Cascading Web Page Templates

For managing web page layouts
By Joe Hubert

Overview

The physical layout and cosmetic design of a web site requires skill and effort that is often beyond or not in line with the responsibilities of a typical software developer. Approaches have been taken to separate coding logic from the web page view. The tiles (link) framework and JSP templates are examples of approaches that address easing maintenance and reusability of web page layouts. But I believe these approaches are a bit backwards. They are built around the concept of template pages that determine the layout of content and directly pull in the relevant "sub-pages". So these templates take on the responsibilities of how the types of content should be laid out and what content should be included in each page. This document proposes another approach where the template determines the layout but the "child pages" determine the specific content and insert it into the master template.

Trouble with the Typical Approach

As I see it, the trouble with the typical approach applies to all types of web pages (jsp, asp, html, etc.). I will use *.jsp pages in this example. Consider this simple template:

Listing 1: template.jsp
<html>
    <head>
        <title><!-- title here --></title>
    </head>
    <body>
        <div id="header">
              <!-- header content here -->
        </div>

        <div id="body">
              <!-- body content here -->
        </div>

        <div id="footer">
              <!-- body content here -->
        </div>
    </body>
</html>

Let's say we want to use this template to create a web page of recipes. We could make a copy of this template for each recipe and include the relevant content sub-pages in each:

Listing 2: eng-muff.jsp
<html>
    <head>
        <title>English Muffin</title>
    </head>
    <body>
        <div id="header">
              <%@ include file="./eng-muff-header.jsp" %>
        </div>

        <div id="body">
              <%@ include file="./eng-muff-recipe.jsp" %>
        </div>

        <div id="footer">
              <%@ include file="./eng-muff-footer.jsp" %>
        </div>
    </body>
</html>

Listing 3: lasagna.jsp
<html>
    <head>
        <title>Lasagna</title>
    </head>
    <body>
        <div id="header">
              <%@ include file="./lasagna-header.jsp" %>
        </div>

        <div id="body">
              <%@ include file="./lasagna-recipe.jsp" %>
        </div>

        <div id="footer">
              <%@ include file="./lasagna-footer.jsp" %>
        </div>
    </body>
</html>

You can imagine how the content of the different included files will result in different composite JSP files (recipes for English muffins, lasagna, etc.). If you can't imagine it, maybe this helps:

Recipe English Muffin (header)

(body)
  1. Split muffin in half
  2. Place in toaster, press to toast
  3. Apply butter, serves 1


Jam or jelly optional (footer)

Now let's say create a new web page template that has all sorts of new div's and images and looks just great. You want to use this new template and place all your existing content in it. With the approach above, there is a file for every recipe. Since the html layouts are different, a lot of work has to be done to re-wrap each recipe with the new layout. The basis of the problem is this: the “template" was more than a boilerplate as to how content should be arranged - - it determined what specific content should be included in each of files that were based on it.

A Better Way

This document proposes an approach that would address this problem. Let's re-look at the example above starting with the template:

Listing 4: template.cwpt
<html>
    <head>
        <title>{{=$title}}</title>
    </head>
    <body>
        <div id="header">
            {{=#header}}
        </div>

        <div id="body">
            {{=#body}}
        </div>

        <div id="footer">
            {{=#footer}}
        </div>
    </body>
</html>

The obvious change is that this template no longer has “inlclude" directives in it. These notations, which I've made up arbitrarily:

{{=#header}}
{{=#body}}
{{=#footer}}
represent placeholders for content that should be inserted into the JSP template. Much like the mail merge paradigm, this file is meaningless from a content point of view by itself. It needs a source of data to drive its content.

Here's how the recipe files would look:

Listing 5: eng-muff.cwpt
<@merge into="./template.cwpt" dest="./eng-muffin.jsp>

#{{
    $title = “English Muffin Recipe"
}}

#header {{
    <%@ include file="./eng-muff-header.jsp" %>
}}

#body {{
    <%@ include file="./eng-muff-recipe.jsp" %>
}}

#footer {{
    <%@ include file="./eng-muff-footer.jsp" %>
}}
Listing 6: lasagna.cwpt
<@merge into="./template.cwpt" dest="./lasagna.jsp>

#{{
    $title = “Lasagna Recipe"
}}

#header {{
    <%@ include file="./lasagna-header.jsp" %>
}}

#body {{
    <%@ include file="./lasagna-recipe.jsp" %>
}}

#footer {{
    <%@ include file="./lasagna-footer.jsp" %>
}}

Since the content files don't have any concept of the web page layout, a completely different layout for all the recipes could be achieved by creating a new template.cwpt file.

How is this supposed to work?

Most the mechanics of this approach involve constructs that don't exist yet. Let me explain how this is intended to work.

@merge directive

The merge directive would tell a pre-processor to merge its contents into the template file indicate by the “into" attribute and create a resulting file named by the “dest" attribute.

#(var) {{ ... }} constructs

These source files define content constructs and give them names. These names will match the placeholder names in the “merge into" template.

#{{$(var)=(value) }} constructs

These constructs are used to define string variables that can be merged into a template, similarly to how html content blocks are merged.

So the contents of the eng-muff.cwpt file would be merged by a pre-processor into the template.cwpt file. The placeholders in that file would be replaced as follows:

Template file: template.cwpt Content file: eng-muff.cwpt Resulting file: eng-muffin.jsp
<html>
    <head>
<@merge into="./template.cwpt" dest="./eng-muffin.jsp>
<html>
    <head>
<title>{{=$title}}>/title>
#{{
    $title = “English Muffin Recipe"
}}
       <title>{{=$title}}</title>
    </head>
    <body>
        <div id="header">
 
    </head>
    <body>
        <div id="header">
            {{=#header}}
#header {{
    <%@ include file="./eng-muff-header.jsp" %>
}}
            <%@ include file="./eng-muff-header.jsp" %>
        </div>

        <div id="body">
 
        </div>

        <div id="body">
            {{=#body}}
#body {{
    <%@ include file="./eng-muff-recipe.jsp" %>
}}
            <%@ include file="./eng-muff-recipe.jsp" %>
        </div>

        <div id="footer">
 
        </div>

        <div id="footer">
            {{=#footer}}
#footer {{
    <%@ include file="./eng-muff-footer.jsp" %>
}}
            <%@ include file="./eng-muff-footer.jsp" %>
        </div>
    </body>
</html>
 
        </div>
    </body>
</html>

Cascading Web Page Template Terminology and Rules

  • The pre-compiler will generate an output file for every content file that includes a “dest" attribute in its merge directive. (see exception below on intermediate template files with destination attributes)
  • The corresponding content and template files involved in this arrangement will be referred to as a merge sequence. The file containing the definitions for the named variable constructs will be considered the content file and the file into which this content is merged will be considered the template file. In the example above, there is a merge sequence involving the content file, eng-muff.cwpt, and the template file, template.cwptl. The result of pre-compilation is the destination file, eng-muffin.jsp.
  • There may be more than two files involved in a merge sequence. File 1, a content file, may be merged into File 2, a template file that also defines named variable constructs. File 2 may be subsequently merged into File 3. In this case we will refer to File 2 as an intermediate template file. In the context of File 3, it is the content file for that step in the merge sequence.
  • When more than two files are involved in a merge sequence, the pre-compiler must determine and execute the proper merge sequence (merging File 1 into File 2 and then merging that result into File 3). The lowest level content files will be merged into their immediate templates first, and those results will be merged up the chain of the merge sequence.
  • If no named variable construct is provided for the corresponding placeholder in a template file, that section will be blank in the generated destination file. The pre-compiler will allow this and present warnings based on compilation settings. (consider: should configuration allow this to be considered an error instead of a warning?)
  • If a named variable construct in a content file has no corresponding placeholder in any of its successive templates in its merge sequence, the pre-compiler should issue an error (consider: should configuration allow this to be considered a warning instead of an error?)
  • If an intermediate template file has a destination target file attribute in its merge directive, the pre-complier will issue a warning. A file will not be generated for the intermediate template file, only by the target of the initial (lowest level) content file.
  • In the case of merge sequences involving more than two files, named variable constructs from lower level content files will override those of their merge files. Consider the case where File 1 is merged into File 2 and that result is merged into File 3. If File 3 contains a named placeholder for “footer", and File 1 and File 2 define content for “footer", the destination file will contain the content defined in File 1. This provides a means of subclassing content files. (Consider the need or use of a “super" keyword that would allow content from the “footer" in File 1 to be appended to the “footer" in File 2.
  • The contents of a named variable construct should be any Unicode character set (static html content, web page scripting, include directives, etc.)

Other Notes

  • The purpose of this proposal is to provide a pre-compilation for the merger of web pages. This proposal does not take the place of any MVC mechanism, CMS, or tag libraries. It should increase in the manageability of those supplementary technologies serving as a lightweight mechanism for page construction. I would expect this idea would be especially beneficial to anybody selling web page templates. It makes complicated page layouts easy to use and incorporate.
  • A scripting or expression language should not be relevant to this idea.
  • This framework should apply to any web page language (JSP, ASP, php, HTML, etc.).

Supporting Ideas

In order to realize this idea without any changes to the JSP specification, the following tools would be practical.

ANT Task

The ANT task should merge all .cwpt files into their JSP (or whatever) targets and move these files to the appropriate destination folder in the web application project.

Eclipse Plug-In

An Eclipse plug-in that brought the files together on a virtual page would assist with page creation. The editor in the plug-in should arrange and identify the different file components in the merge sequence.

Feedback

Articles
Factory 101

Strategy 101

Cascading Web Page Templates