Go templates

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? In the interest of limiting third-party Javascript running on this site, I've disabled all commenting plugins. If you have feedback, please share it with me on Twitter.

Elsewhere

Twitter (@kurrik) Github (kurrik) Google+ (+kurrik) Linkedin (kurrik)

Tags

reviews (9) arne (8) chrome (7) work (6) extensions (6) games (5) cinemaclub (5) twitter (5) html (4) google (3) presentations (3) algorithms (3) books (3) javascript (3) go (3) internet (2) ludumdare (2) estonia (2) appengine (2) space (1) readinglist (1) recipes (1) management (1) http (1)