paulgorman.org/technical

Golang Templates

Go provides two similar template packages, one for general text and another for HTML. The HTML package works essentially the same way at the text templates, but protects against some web-specific security concerns.

The overview of the text/template packages is a good starting point.

Execute a template by applying it to a data structure.

package main

import (
	"os"
	"text/template"
)

type Inventory struct {
	Material string
	Count    uint
}

func main() {
	planet := "Mars"
	tmpl, _ := template.New("launch").Parse("Launch the mission to {{.}}!\n")
	tmpl.Execute(os.Stdout, planet)

	sweaters := Inventory{"wool", 17}
	tmpl, _ = template.New("itemcount").Parse("{{.Count}} items are made of {{.Material}}.\n")
	tmpl.Execute(os.Stdout, sweaters)
}

Execution of the template walks the data structure. Annotations ({{foo}}) in the template refer to elements of the data structure. Execution of the template sets a cursor to the value at its current location as it walks the data structure. A dot ({{.}}) represents the cursor.

The second parameter of Execute sets the cursor’s initial value.

We can manually change the current context using the with action:

{{with pipeline}} T {{end}}

A template is UTF-8 text. All text is output verbatim, except for “actions” enclosed between {{ and }}. Well, not necessarily verbatim — {{.x -}} × {{- .y}} trims the leading and trailing whitespace around × in the output.

Actions include loops, conditionals, and variable assignment:

{{/* A comment. */}}

{{define "embeddedtemplate"}} Thing: {{.}} {{end}}

{{template "embeddedtemplate" .Foo}}

{{with .MyValue}} A thing. {{else}} Nothing {{end}}

<h1>{{.Title}}<h1>
<ul>
{{range .Todos}}
	{{if .Done}}
		<li class="done">{{.Title}}</li>
	{{else}}
		<li>{{.Title}}</li>
	{{end}}
{{end}}
</ul>

<ol>
{{range .Steps}}
	{{$s := .Name}}
	<ol>
	{{range .SubSteps}}
		<li><b>{{$s}}:</b> {{.}}</li>
	{{end}}
	</ol>
{{end}}
</ol>

The template action executes the named template, embedded within the current template (i.e., an “include”). Name templates using the define action. The template action may be executed with nil data or we can pass in data from a pipeline:

{{template "name"}}
{{template "name" pipeline}}

Go templates have a “pipeline” concept, similar to a unix shell pipeline. In a Go template, a pipeline might be a single simple value or a function call with multiple arguments. Combine a sequence of chained commands with |; the output from one commands becomes the input to the next.

Parse a template from a file:

tmpl := template.Must(template.ParseFiles("foo.html")

(Note: “Since the templates created by ParseFiles are named by the base names of the argument files, t should usually have the name of one of the (base) names of the files. If it does not, depending on t’s contents before calling ParseFiles, t.Execute may fail. In that case use t.ExecuteTemplate to execute a valid template.”)

Use a template in an http request handler:

func(w http.ResponseWriter, r *http.Request) {
	tmpl.Execute(w, data)
}

Go templates can capture the results of a pipeline in a variable. Template variables start with $.

$variable := pipeline
range $index, $element := pipeline

Caching Templates

For efficiency, parse templates once only, then execute them as needed. Since ParseFiles is variadic, have it parse all the files in one go and save them to a slice.

var templates = template.Must(template.ParseGlob(filepath.Join("templates", "*.html")))

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
    err := templates.ExecuteTemplate(w, tmpl, p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}