Golang Template Inheritance (Python Jinja2 extends & include)
Introduction
This posts will show how to do template inheritance via Go official html/template package. There are other template engines in Go, such as jigo, pongo2, or mustache, but I like to use official packages as much as possible.
We will introduce template inheritance by three steps:
- Define a base skeleton template that contains all the common elements of your site and defines blocks that child templates can override.
- include another templates in the base skeleton template.
- Define a child template that extends the base skeleton template and output the final HTML we need.
Template Hierarchy
Assume we have 4 templates as follows:
tmpl/index.html
tmpl/layout/layout.html
tmpl/layout/includes/metaog.html
tmpl/layout/includes/footer.html
- layout.html is the base template (skeleton)
- metaog.html and footer.html will be included in layout.html
- index.html is the child template that extends layout.html
Look at layout.html (base skeleton template) first:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | {{define "layout"}} <!doctype html> <html prefix="og: http://ogp.me/ns#"> <head> <meta charset="utf-8"> <title>{{.SITENAME}}</title> <meta name="viewport" content="width=device-width, initial-scale=1"> {{template "metaog" .}} </head> <body> {{block "content" .}}{{end}} {{template "footer" .}} </body> </html> {{end}} |
In line 8 and 14 of layout.html, two templates(metaog and footer) are included via template action, which is roughly the same as the Jinja2 include tag.
In line 12 of layout.html, a block named content is declared and will later be overridden by child template. The block here is also roughly the same as the Jinja2 blocks.
Next, look at the two tempaltes included in base skeleton:
1 2 3 4 | {{define "metaog"}} <meta property="og:title" content="{{.SITENAME}}"> <meta property="og:url" content="{{.SITEURL}}"> {{end}} |
1 2 3 4 5 | {{define "footer"}} <div>Powered by <a href="https://golang.org/">Go</a> </div> {{end}} |
Nothing special above. The syntax is quite intuitive and easy to understand.
Finally, we will see how the child template (index.html) extends the base template (layout.html) and override the content block in the base template:
1 2 3 4 5 | {{template "layout" .}} {{define "content"}} <div>Hello World!</div> <div>Template Inheritance in Go html/template</div> {{end}} |
The effect of template action in the first line is the same as the Jinja2 extends tag, and from line 2 to last line, the content block is defined and override the declaration in the base template.
Template Rendering
The following ParseTemplateDir function reads all the above 4 templates under tmpl directory: [5]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package gossg import ( "html/template" "os" "path/filepath" ) func ParseTemplateDir(dir string) (*template.Template, error) { var paths []string err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { paths = append(paths, path) } return nil }) if err != nil { return nil, err } return template.ParseFiles(paths...) } |
Define the template data and render index.html template to generate final HTML output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package gossg import ( "os" "testing" ) type TemplateData struct { SITENAME string SITEURL string } func TestTemplateToHtml(t *testing.T) { data := TemplateData{ SITENAME: "Theory and Practice", SITEURL: "https://siongui.github.io/", } tmpl, err := ParseTemplateDir("tmpl") if err != nil { t.Error(err) } err = tmpl.ExecuteTemplate(os.Stdout, "index.html", &data) if err != nil { t.Error(err) } } |
Final HTML output:
=== RUN TestTemplateToHtml
<!doctype html>
<html prefix="og: http://ogp.me/ns#">
<head>
<meta charset="utf-8">
<title>Theory and Practice</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:title" content="Theory and Practice">
<meta property="og:url" content="https://siongui.github.io/">
</head>
<body>
<div>Hello World!</div>
<div>Template Inheritance in Go html/template</div>
<div>Powered by
<a href="https://golang.org/">Go</a>
</div>
</body>
</html>
--- PASS: TestTemplateToHtml (0.00s)
PASS
Tested on:
- Ubuntu Linux 16.10
- Go 1.7.5
References:
[1] | Golang html/template versus Python Jinja2 (6) - Template Inheritance (Extends) |
[2] | [Golang] Example for block Action in Template package |
[3] | golang arguments dot - Google search golang arguments dot - DuckDuckGo search golang arguments dot - Bing search golang arguments dot - Yahoo search |
[4] | [Golang] Walk All Files in Directory |
[5] | Golang Template Parse Directory |
[6] | Creating a simple website, how to use the out of the box library "html/template" to create a layout and its children? : golang |