Template systems like Twig in PHP and Jinja in Python allow for templates to inherit from a base template. In Freemarker you can do something similar but you have to set it up yourself using the macro system. At work we are using Freemarker with Dropwizard. I was familiar with Twig and was hoping that we can get a simliar inheritance system in Freemarker. It's not in the docs, but it is possible to have the feature.

Freemarker lets you do some neat stuff. You can have a "base" template that defines the overall page structure, and then the actual template you want to write can just fill in the pieces. Here is how that will look:

base.ftl:
<#macro page_head>
  <title>Page title!</title>
  <link rel="stylesheet" href="/css/default.css">
</#macro>

<#macro page_body>
  <h1>Basic Page</h1>
  <p>This is the body of the page!</p>
</#macro>

<#macro display_page>
  <!doctype html>
  <html>
    <head> 
      <@page_head/> 
      <script type="text/javascript" src="/js/tracking.js"></script>
    </head>
    <body>
      <div id="nav"><a href="/">Home</a></div>
      <@page_body/>
    </body>
  </html>
</#macro>
user.ftl:
<#include "base.ftl">
<#macro page_head>
  <title>User page</title>
  <script src="/js/user.min.js"></script>
</#macro>

<#macro page_body>
  <h1>User</h1>
  <p>The user page!</p>
</#macro>

<@display_page/>

Freemarker provides a powerful macro system that allows us to implement template inheritance. In base.ftl we define a few macros for the head and body of the page. Then we define the display_page macro to wrap the head and body. In display_page we get to define the basic structure of the page and include common things, like a user tracking code or page navigation. Templates can define what goes in the head and body by extending the base file, define their own macro named page_head, page_body, this overrides the macros defined in base.tpl, then call the wrapping macro display_page. There can be more than one base template, and different page templates can include the one that works best for them. So we could have a public_base.ftl and a admin_base.ftl that define different wrappers but the individual templates still define their own head, body, etc.

In other template systems you are provided a parent or extends method along with block tags to build up this kind of structure. In Freemarker it is still possible but you have to jump through the hoop of setting up macros to do it for you.

Default Values

In the above example, user.ftl redefines the page_head and page_body macro. If the page_head macro was not defined in user.ftl, then Freemarker would default to the macro defined in base.ftl. What if we want access to that macro in user.ftl, and we want to incorporate it into our own page_head macro? In the Twig template system you can use the {{ parent() }} tag to include it. In Freemarker, once we overwrite it, it is gone. So we need to get around it manually.

The simplest way to do this is to define another macro and give it a prefix. I'll make a page_head and common_page_head. In the base template we define the default page_head to just include common_page_head and if we redefine the macro in a child template then we can call the common macro if we need it, recreating the feature of {{ parent() }}.

base.ftl:
<#macro common_page_head>
  <meta charset="utf-8">
</#macro>

<#macro page_head>
  <@common_page_head/>
</#macro>

<#macro page_body>
  <h1>Basic Page</h1>
  <p>This is the body of the page!</p>
</#macro>

<#macro display_page>
  <!doctype html>
  <html>
    <head> 
      <@page_head/> 
      <script type="text/javascript" src="/js/tracking.js"></script>
    </head>
    <body>
      <div id="nav"><a href="/">Home</a></div>
      <@page_body/>
    </body>
  </html>
</#macro>
user.ftl:
<#include "base.ftl">
<#macro page_head>
  <@common_page_head/>
  <title>User page</title>
  <script src="/js/user.min.js"></script>
</#macro>

<#macro page_body>
  <h1>User</h1>
  <p>The user page!</p>
</#macro>

<@display_page/>

Now that I have a common_page_head macro, I can put anything common to all the page heads in there. By default, if a template does not define a page_head macro we get common_page_head output. If a template does redefine the head macro then they have the option of including the common stuff. It's kind of annoying to have to define these extra macros when other template systems have the functions built in. But Freemarker is a popular system in Java and I don't see any other major template library that has inheritance built in.

I hope this article was useful. :-)


Comments

comments powered by Disqus