When I started learning about Golang templates much of what I read was around their syntax and feature set. It didn’t take long, but I found myself struggling to do things that struck me as being pretty basic. So I figured I’d write down a few things I’ve learned.

By no means is this intended to be a proper (or even correct) howto on Golang templates, rather it’s just what I’ve learned so far. Here’s what I was trying to accomplish:

Have a directory of templates ( header.html , foobar.html , etc). Have a directory of static files (css, images, etc). Use some templates as full pages ( about.html , hello.html ). Use some templates as partials ( header.html , footer.html ). Serve static content in a manner similar to http.FileServer. Exclude templates from the static files being served. Support custom template functions. Compile everything into a single static binary (including templates and static files).

What I used

Here’s a quick punch list of the things I used and what they are for.

Tool Purpose Golang 1.5 Compiler go generate Precompiler github.com/jteeuwen/go-bindata Code generation github.com/elazarl/go-bindata-assetfs http.Dir for use with http.FileServer github.com/julienschmidt/httprouter Mux Handler

Installation

go get github.com/jteeuwen/go-bindata/... go get github.com/elazarl/go-bindata-assetfs/... go get github.com/julienschmidt/httprouter

Directory structure

The goal was to wind up with something like this:

. ├── functions.go ├── main.go ├── templates │ ├── footer.html │ ├── header.html │ └── hello.html └── static └── golang.png

Inside the static folder I’d put static assets, things like pictures, css, javascript. Inside templates I’d put the full and partial templates, and template functions would go inside functions.go . The application itself would then be main.go .

Functions.go

A hello world example might look like this, with a single function to uppercase something:

package main import ( "strings" "html/template" ) var ( templateMap = template . FuncMap { "Upper" : func ( s string ) string { return strings . ToUpper ( s ) }, } )

Templates

templates/header.html

< html > < head > < title >{{ .Title }}</ title > </ head > < body >

templates/hello.html

{{ template "templates/header.html" . }} Hello world ! My name is {{ . Name | Upper }} {{ template "templates/footer.html" . }}

templates/footer.html

</ body > </ html >

Sample hello world application

main.go :

package main //go:generate go-bindata-assetfs static/... templates/... import ( "html/template" "log" "net/http" "github.com/julienschmidt/httprouter" ) // Model of stuff to render a page type Model struct { Title string Name string } // Templates with functions available to them var templates = template . New ( "" ). Funcs ( templateMap ) // Parse all of the bindata templates func init () { for _ , path := range AssetNames () { bytes , err := Asset ( path ) if err != nil { log . Panicf ( "Unable to parse: path=%s, err=%s" , path , err ) } templates . New ( path ). Parse (string( bytes )) } } // Render a template given a model func renderTemplate ( w http . ResponseWriter , tmpl string , p interface {}) { err := templates . ExecuteTemplate ( w , tmpl , p ) if err != nil { http . Error ( w , err . Error (), http . StatusInternalServerError ) } } // Well hello there func hello ( w http . ResponseWriter , r * http . Request , ps httprouter . Params ) { model := Model { Name : ps . ByName ( "name" )} renderTemplate ( w , "templates/hello.html" , & model ) } // The server itself func main () { // mux handler router := httprouter . New () // Example route that takes one rest style option router . GET ( "/hello/:name" , hello ) // Serve static assets via the "static" directory router . ServeFiles ( "/static/*filepath" , assetFS ()) // Serve this program forever log . Fatal ( http . ListenAndServe ( ":8080" , router )) }

Build the application

go generate go build && ./golang-templates-example

Then you can use something to test it:

$ curl -s localhost:8080/hello/jack <html> <head> <title></title> </head> <body> Hello world! My name is JACK </body> </html>

Overview

go-bindata does a great job of compiling static assets into a single binary, and go-bindata-assetfs makes it easy to serve those assets over http. I wasn’t able to find an easy way to expose ExecuteTemplate in a manner that made it possible to add a function map, but a quick for loop in init() seems to do the trick. Partials are managed here purely using upstream documentation. I have not yet sorted out how to exclude the templates from being served over http (hence the strike through). A working example if this sample project is on Github:

https://github.com/jmcfarlane/golang-templates-example

For those of you that actually know how to do this stuff, I’d love to hear from you (via pull request or whatever).

Cheers!