KyleHQ

Honest, loyal, senior software engineer, looking to make a real difference by putting people first.

KyleHQ © 2024

Golang Templates - What I missed!

May 21, 2017
|
code

TLDR

Don’t include {{define}} calls in your templates parsed with template.ParseFiles(). Know that template.ParseFiles() will overwrite any reference to a template with the same filename, not filename and path! Go look at the way I now parse my application templates here - https://gitlab.com/snippets/1662623. Otherwise feel free to stick around and read why =]

So what’s the issue?

Having happily built out the backend of a little Golang app I’ve been working on, I’ve refocused my attention to the frontend. In general, I thought I had a ok understanding of how my html templates were being referred to and rendered. But of course all that changed when I wanted to add a partial to my main application layout.

As you read on, I will refer back to the official docs for some aha moments. To be honest however, I haven’t found the official docs terribly helpful in my day to day template usage. Yes of course “everything is in there”, but I’m yet to find a good online resource with examples for Go templating? In fact a lot of my templating knowledge came from this website which is built with Hugo! Let’s see if I can shed a little more light on the subject with this contrived example.

A typical setup?

main.go
templates
├── _footer.html
├── _header.html
├── layout.html
├── sign
│   ├── index.html
├── templates.go
└── user
    ├── dashboard.html
    ├── index.html

// And here are the contents of the layout.html file. {{define "app/layout"}} <html> <head> </head> <body> <h1>Layout</h1> {{yield}} </body> </html> {{end}}

With this single layout, we have defined one template func yield which will be used to render an inner templates contents. Of course to get the most out of my apps, I parse all of the available templates on the init() call. This means on execute, we only need to parse the data to render the template. The structure above and layout contents look fairly innocuous but I’m sure to the seasoned trained eye, you can see a couple problems.

How many layouts???

To parse the layout, we may use this code block

var (
	layout *template.Template
	layoutFuncs = template.FuncMap{
		"yield": func() (string, error) {
			return "", fmt.Errorf("yield called unexpectedly.")
		},
	}
)

func init() { layout = template.New("layout").Funcs(layoutFuncs) layout = template.Must( layout.ParseFiles("./templates/layout.html"), ) }

We create a package level var of layout and we populate this value with layout = template.New("layout").Funcs(layoutFuncs). By assigning a default template func, the code will at least compile rather than error with function "yield" not defined. Then we attempt to parse our actual layout file with template.Must(layout.ParseFiles("./templates/layout.html")). Note the use of template.Must() which will panic on any error.

The interesting thing about this example call is that layout does not now contain one template instance, it actually contains three! Let’s check this by ranging over the layout templates with this extremely useful snippet.

for _, t := range layout.Templates() {
    fmt.Println(t.Name())
}

// Which would output ~$ go run main.go layout.html app/layout layout

The layout and the layout.html made sense to me. One created by the template.New("layout") call, the other being the filename returned on template.ParseFiles(). But the third value of app/layout showed a hole in my knowledge. It’s due to using the define keyword in the layout

// And here are the contents of the layout.html file.
{{define "app/layout"}}
<html>
<head>...

This also defines a new template as denoted in the docs. Of course writing it here it’s plain as day but my confusion was well founded as it stemmed from a book I referenced early on in my Go development. Here’s an excerpt

As you can see, we have an html file layout with an embedded {{define}} call. Effectively we are now parsing a file template that has an embedded template to parse too! Of course you’re free to refer to your templates this way but personally I wouldn’t. I much rather computing less and using the explicit value of the filename. Especially with partials eg;

<!DOCTYPE html>
<html>
<head></head>
<body>
{{template "_header.html"}}

Sorry how many templates???

Ok so far so good, I’ve removed all/any of my {{define calls inside my html files. Now lets parse the rest of my templates using the templates.ParseGlob() function.

main.go
templates
├── _footer.html
├── _header.html
├── layout.html
├── sign
│   ├── index.html
└── user
    ├── dashboard.html
    ├── index.html

templates = template.New("templates") templates = template.Must( templates.ParseGlob("./templates//.html"), )

The pattern ./templates//.html tells ParseGlob to find any folder nested inside the templates directory and find any file with an .html extension. From the directory structure above, you’d be forgiven for thinking you will have four templates. One for the initial New() call of templates, one for index.html in the sign directory, and another x2 files in the user directory. But you be wrong! Lets foreach and see what we get back

// Only THREE templates!
~$ go run main.go
templates
index.html
dashboard.html

The docs are pretty clear on this one, but it’s still a trick for young players. Any duplicate files found will be overridden. The last file trumps and this came as a real surprise to me! In all my experience developing thus far, a template has always been referred to by its name and its path to stop overwrites from occurring.

A solution?

Ok so now we know a little more, let’s see if we can get the desired outcome. We know that the ParseGlob hands off to the _parseFiles which in turn will return the basename on any file found. We could write our own _parseFiles method and return what we need, but I have a better solution. Instead you can use the "path/filepath" package and walk the files directly. On each file you find, you can call New() on a template instance and then name the template to whatever you like! Something similar to

const trimPrefix = "templates/"

ts, err := filepath.Glob("./templates//.html") if err != nil { log.Fatal(err) }

templates = template.New("templates") for _, t := range ts { b, err := ioutil.ReadFile(t) if err != nil { log.Fatal(err) } name := strings.TrimPrefix(t, trimPrefix) template.Must(templates.New(name).Parse(string(b))) }

This way I name each of my files to that of the filename and path removing the prefix “/templates”. So templates/sign/index.html becomes sign/index.html, much better! If you want the full example of how I am currently rendering my application templates, head over to the snippet https://gitlab.com/snippets/1662623

Wrap up

Initially I thought I was going nuts when trying to render my application templates. But after a little more digging and a little more reading I’m happy with the solution I’ve thus far settled with. Hopefully my learnings can be used to help another would be Gopher!

References

The culprit of single template names https://golang.org/src/text/template/helper.go

The excerpt came from this book which was a great introduction into programming with Go Level up your web app with Go. But like all books, the art of doing is a much better teacher!