Go’s html template package has some really powerful safety features but is unfortunately not designed to be as simple as some of the other template packages I’ve used in the past.
In most templating packages I’ve seen, inheritance is expressed inside of the
template itself. So if you have two pages index
and post
which inherit
from a common root
, the inheritence is expressed inside of the templates
themselves. It’s sufficient to parse index
and post
in code, and let the
templating engine handle the lookup of root
on its own:
Go’s templates don’t allow a template to specify which template(s) it inherits from, only ones which the root template calls. Therefore the association points downstream, which means that you call render on the parent.
Therefore inheritance needs to be managed in code. Luckily, a template exposes
a Clone
method which allows you to copy a parsed template and then populate
any empty sections on a per-clone basis.
It’s easy enough to define a simple class which allows you to parse a set of
files as a root
template and then split that into new templates as needed.
Following is a rough example. Instantiate it by calling
NewTemplatesFromGlob
, specifying a glob pattern which defines the entire set
of base templates:
type Templates struct {
tmpl map[string]*template.Template
}
func NewTemplatesFromGlob(glob string) (t *Templates, err error) {
var (
root *template.Template
)
if root, err = template.New("root").ParseGlob(glob); err != nil {
return
}
t = &Templates{
tmpl: map[string]*template.Template{
"root": root,
},
}
return
}
func (t *Templates) Split(from string, to string) (err error) {
var (
tmpl *template.Template
)
if tmpl, err = t.tmpl[from].Clone(); err != nil {
return
}
t.tmpl[to] = tmpl
return
}
func (t *Templates) Get(key string) (tmpl *template.Template) {
return t.tmpl[key]
}
For a concrete example, assume you have the following template structure:
templates/root/base.html
This is the base all clones will inherit from. Note that the root
template
is explicitly defined, as ParseFiles
seems to break if it isn’t (even though
you should be able to have a single unwrapped template - this may be a bug with
Go’s library).
Note the empty head
and body
templates. Because they’re empty, they’ll be
overrideable in clones. Trying to override a populated template will
unfortunately cause an error.
{{define "root"}}<!DOCTYPE html>
<html>
<head>
{{template "head" .}}
</head>
<body>
{{template "body" .}}
</body>
</html>
{{end}}
{{define "head"}}{{end}}
{{define "body"}}{{end}}
templates/root/post.html
For the sake of the example, there’s another template included in the base
called post
. This can be called from any cloned template, so it shows how
you can make a set of common templates which may be called by individual
overrides.
{{define "post"}}
<div class="post">
<h2>{{.Title}}</h2>
{{.Body}}
</div>
{{end}}
templates/index.html The index page just defines a simple body.
{{define "body"}}
<h1>Index</h1>
<p>Hi! Check out the <a href="/posts">posts</a></p>
{{end}}
templates/posts.html
The posts page iterates over the data and calls the post
base template.
{{define "body"}}
<h1>Here are posts</h1>
<div class="posts">
{{range .Posts}}
{{template "post" .}}
{{end}}
</div>
{{end}}
Building the templates in code requires parsing the files in templates/root
and then splitting that into individual posts
and index
clones, then
calling ParseFiles
to override the empty body
template in root
.
The beauty of Go is that this is probably clearer when expressed in code:
var (
err error
templates *Templates
)
if templates, err = NewTemplatesFromGlob("templates/root/*.html"); err != nil {
// Handle error
}
if err = templates.Split("root", "posts"); err != nil {
// Handle error
}
if _, err = templates.Get("posts").ParseFiles("templates/posts.html"); err != nil {
// Handle error
}
if err = templates.Split("root", "index"); err != nil {
// Handle error
}
if _, err = templates.Get("index").ParseFiles("templates/index.html"); err != nil {
// Handle error
}
Rendering is very straightforward and relies on the built in Execute
method.
Just Get
the template you want to render and call the built-in package as you
would normally:
var (
out string
writer *bytes.Buffer
data = map[string]interface{}{
"Posts": []map[string]interface{}{
map[string]interface{}{
"Title": "Foo",
"Body": "Bar",
},
},
}
)
writer = bytes.NewBufferString("")
if err = templates.Get("index").Execute(writer, data); err != nil {
// Handle error
}
out = writer.String()
fmt.Printf("INDEX:\n%v\n", out)
writer = bytes.NewBufferString("")
if err = templates.Get("posts").Execute(writer, data); err != nil {
// Handle error
}
out = writer.String()
fmt.Printf("POSTS:\n%v\n", out)
This produces the following output:
INDEX:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Index</h1>
<p>Hi! Check out the <a href="/posts">posts</a></p>
</body>
</html>
POSTS:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Here are posts</h1>
<div class="posts">
<div class="post">
<h2>Foo</h2>
Bar
</div>
</div>
</body>
</html>
You can build more complicated template hierarchies by adding wrapper templates. In the following diagram, I’ve called them variants:
Imagine you have a set of pages which depend on an administrator menu which you
wish to insert above or wrapping the body
content. The most direct approach
I’ve discovered involves creating another layer of template and then creating
both regular and admin variants.
templates/regular.html
{{define "body_content"}}{{end}}
{{define "body"}}
{{template "body_content" .}}
{{end}}
templates/admin.html
{{define "body_content"}}{{end}}
{{define "body"}}
<div class="admin_menu">
Admin menu
</div>
{{template "body_content" .}}
{{end}}
Then every page living in the third tier of templates must implement
body_content
instead of body
. Because body_content
is used consistently,
you could theoretically make any page an admin
page just by changing its
variant and reparsing.
It’s honestly not the most intuitive system. I’m not really sold on the utility of managing template inheritance through code. I do believe that it should be possible to extend the rudimentary template management framework I’ve included here into a real library which would be able to infer inheritance through some sort of template file metadata. For example, imagine a directory structure like the following:
templates/
+- base.html
+- post.html
+- admin/
| \- admin.html
\- regular/
+- index.html
\- posts.html
All of the inhertance could be inferred by the directory path, which would keep template logic contained to the template files and organization themselves.
Eventually someone may port a more sophisticated system to Go, but it would
lose a lot of the attractive security features already implemented in
html/template
. Learning to juggle clones may be the best way to present
untrusted data in HTML format to your website’s users.
Comments? If you have feedback, please share it with me on Twitter!