ExampleCode

Example

Here is a short, simple example to demonstrate how mtemplate's new directives are used.

I am developing a website for the Shazowie Magic Shoppe. The site will have branding elements that are common to each page and different content inside the branding area. The branding skeleton is located in branding.html:

<html>
  <head>
    <title>Shazowie Magic Shoppe - {.child page_title}</title>
    {.child js_libs}
  </head>
  <body>
    <img src="/imgs/banner.jpg">
    <h1>Shazowie Magic Shoppe</h1>
    <h2>Your one-stop shop for esoterica and some other stuff</h2>
    <br/>
    <h3>{.child page_title}</h3>
    {.child}
  </body>
</html>

The .child directives are notable because they will be replaced with content from the child template. Some of the .child directives have names after them: page_title and js_libs. The name indicates that the child template will have .block directives with matching names, and the content of those blocks will be inserted at the parent template's identically-named .child. When a .child directive does not have a name, that means that all of the content in the child template that is not inside a .block should be added.

A few important notes about using .block and .child:

  • The content of a named .block can be added into the parent template as many times as desired. For instance, in our example, {.child page_title} appears twice.
  • If the rendering engine tries to process a named .child directive when the child template does not define an identically-named .block, nothing will occur. That is a valid circumstance; not all child templates will need to provide the same block to the parent, so rendering continues uninterrupted.
  • There is no limit on the depth of parent/child relationships. It is valid for a template that is declared to be the parent of another template to itself declare that another template is its parent.
  • There is absolutely no protection against circular references. If two templates each declare that the other is its parent, the rendering engine will recurse until memory can no longer be allocated and it crashes.

I have a bunch of other pages that I need to create: home.html, faq.html, directions.html. I'd rather not have a separate copy of the branding code in each one, as that is a maintenance nightmare, and even though I am billing by the hour, I want to do the right thing. Here's what faq.html looks like:

{.parent branding.html}
{.block page_title}Frequently Annoying Questions{.end}
{.block js_libs}<script href="/js/jQuery-min.js"></script>{.end}
    <div class="faq">
      <span class="faqq">So, are you guys totally nerdy or what?</span>
      <span class="faqa">NO! For the last time, <i>magic is for cool kids</i>.</span>
    </div>
{.include disclaimer.html}

The {.include disclaimer.html} directive instructs the rendering engine to load, parse and execute the template contained in the file disclaimer.html. Presumably, that information needs to be put onto some pages but not all of them. If the disclaimer were present on all pages, it would be natural to put it in {branding.html}.

disclaimer.html:

    <!-- Legal disclaimer -->
    <div id="disclaimer" style="font-size:xx-small; font-style:italic">
        Nothing on this page is intended in any way to provide actual information.
        For entertainment (magic!) purposes only.
    </div>

Considerations when using .include:

  • There is absolutely no protection against circular references. If a template contains an .include directive that references itself, the rendering engine will recurse until memory can no longer be allocated and it crashes.
  • If the template that is referenced by the .include directive does not exist, the rendering engine will panic. Unlike named .child directives, .include directives are not optional; their absence is considered an error that must be corrected.

In the Google AppEngine for Go code for this application, I only have to worry about rendering the template for the page; the loading, parsing and execution of the parent template is handled automatically.

I would cause the FAQ page to be rendered with one simple API call:

mtemplate.RenderFile("faq.html", wr, data)

where wr is an HttpWriter and data is a map of values that can be inserted into the templates. The code has no dependencies on the content of the templates and does not need to have any knowledge of how the templates are constructed or related. This clean separation of concerns is very helpful for creating robust and easily-maintained applications.

This is what the output from the RenderFile call would look like:

<html>
  <head>
    <title>Shazowie Magic Shoppe - Frequently Annoying Questions</title>
    <script href="/js/jQuery-min.js"></script>
  </head>
  <body>
    <img src="/imgs/banner.jpg">
    <h1>Shazowie Magic Shoppe</h1>
    <h2>Your one-stop shop for esoterica and some other stuff</h2>
    <br/>
    <h3>Frequently Annoying Questions</h3>
    <div class="faq">
      <span class="faqq">So, are you guys totally nerdy or what?</span>
      <span class="faqa">NO! For the last time, <i>magic is for cool kids</i>.</span>
    </div>
    <!-- Legal disclaimer -->
    <div id="disclaimer" style="font-size:xx-small; font-style:italic">
        Nothing on this page is intended in any way to provide actual information.
        For entertainment (magic!) purposes only.
    </div>
  </body>
</html>

Updated

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.