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