Is it possible to have nested templates in Go using the standard library?
Asked Answered
H

5

106

How do I get nested templates like Jinja has in the python runtime. TBC what I mean is how do I have a bunch of templates inherit from a base templates, just filing in blocks of the base templates, like Jinja/django-templates does. Is it possible using just html/template in the standard library.

If that is not a possibility, what are my alternatives. Mustache seems to be an option but would I then be missing out on those nice subtle features of html/template like the context sensitive escaping etc.? What other alternatives are ther?

(Environment: Google App Engin, Go runtime v1, Dev - Mac OSx lion)

Thanks for reading.

Hilbert answered 13/7, 2012 at 9:28 Comment(0)
V
160

Yes it is possible. A html.Template is actually a set of template files. If you execute a defined block in this set, it has access to all the other blocks defined in this set.

If you create a map of such template sets on your own, you have basically the same flexibility that Jinja / Django offers. The only difference is that the html/template package has no direct access to the file system, so you have to parse and compose the templates on your own.

Consider the following example with two different pages ("index.html" and "other.html") that both inherit from "base.html":

// Content of base.html:
{{define "base"}}<html>
  <head>{{template "head" .}}</head>
  <body>{{template "body" .}}</body>
</html>{{end}}

// Content of index.html:
{{define "head"}}<title>index</title>{{end}}
{{define "body"}}index{{end}}

// Content of other.html:
{{define "head"}}<title>other</title>{{end}}
{{define "body"}}other{{end}}

And the following map of template sets:

tmpl := make(map[string]*template.Template)
tmpl["index.html"] = template.Must(template.ParseFiles("index.html", "base.html"))
tmpl["other.html"] = template.Must(template.ParseFiles("other.html", "base.html"))

You can now render your "index.html" page by calling

tmpl["index.html"].Execute("base", data)

and you can render your "other.html" page by calling

tmpl["other.html"].Execute("base", data)

With some tricks (e.g. a consistent naming convention of your template files), it's even possible to generate the tmpl map automatically.

Victualler answered 13/7, 2012 at 9:53 Comment(7)
is it possible to have default data, for example, for "head"?Junction
I'll just add here that in order to render the actual templates I had to call tmpl["index.html"].ExecuteTemplate(w, "base", data).Knuckle
base.html is parsed and stored twice. You can also use the Clone() function as in golang.org/pkg/text/template/#example_Template_shareCanty
I'm having problems when passing data to a nested template. Data from {{ .SomeData }} won't be displayed in the inner template. The outer works.Cheongsam
it matters if template.ParseFiles("index.html", "base.html") is template.ParseFiles("base.html", "index.html")?Crowe
@Cheongsam using the ` . ` in {{ template "temp" . }} makes data available to the nested templateUnwrap
This implies that you have to explicitly parse each pair, correct? I tried using template.ParseGlobe() at first, but realized that the first time something was defined, it was also stored globally. Thus, all templates got the same body.Enface
B
15

note, when you execute your base template, you must pass values down to the child templates, here I simply pass ".", so that everything is passed down.

template one displays {{.}}

{{define "base"}}
<html>
        <div class="container">
            {{.}}
            {{template "content" .}}
        </div>
    </body>
</html>
{{end}}

template two displays {{.domains}} that's passed into the parent.

{{define "content"}}
{{.domains}}
{{end}}

Note, if we used {{template "content"}} instead of {{template "content" .}}, .domains wouldn't be accessible from the content template.

DomainsData := make(map[string]interface{})
    DomainsData["domains"] = domains.Domains
    if err := groupsTemplate.ExecuteTemplate(w, "base", DomainsData); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
Billye answered 30/6, 2016 at 1:57 Comment(9)
Passing down the model is a detail I was stuck on. ;) ThanksOffside
me too - took a bit to figure out :)Billye
What! I can't believe that there was so much meaning in the tiny little dot at the end of the {{template}} placeholder! Why on earth isn't that mentioned anywhere on the tutorials, or even the official Go documentation?? I'm flabbergasted... but also very happy to have found your answer! Thank you very much, now my templates with several levels of nesting work beautifully!Jeffryjeffy
Exactly, the same thing I was trying to figure out!Lucialucian
if we used {{template "content" .}} instead of {{template "content" .}} What's the difference?Gauzy
@iBug, read the answer again, you misquoted it. Pay close attention to the dot.Incumbent
@Incumbent read the answer again, Pay close attention to the comment time and the edit time.Gauzy
@Gauzy Nice work ;)Incumbent
should be called eyeBug since you see myBug with pinpoint accuracy :)Billye
L
9

having worked with other template packages, now a days I mostly work with standard html/template package, I guess I was naive to not appreciate the simplicity it provides and other goodies. I use a very similar approach to accepted answer with following changes

you don't need to wrap your layouts with additional base template, a template block is created for every parsed file so in this case it is redundant, I also like to use the block action provided in new version of go, which allows you to have default block content in case you don't provide one in child templates

// base.html
<head>{{block "head" .}} Default Title {{end}}</head>
<body>{{block "body" .}} default body {{end}}</body>

and you page templates can be the same as

// Content of index.html:
{{define "head"}}<title>index</title>{{end}}
{{define "body"}}index{{end}}

// Content of other.html:
{{define "head"}}<title>other</title>{{end}}
{{define "body"}}other{{end}}

now to execute the templates you need to call it like so

tmpl["index.html"].ExecuteTemplate(os.Stdout, "base.html", data)
Lorola answered 25/10, 2018 at 19:38 Comment(0)
A
4

Use Pongo, which is a super-set of Go Templates that supports the {{extends}} and {{block}} tags for template inheritance, just like Django.

Artful answered 2/4, 2013 at 13:28 Comment(0)
D
4

I've been coming back to this answer for days, finally bit the bullet and wrote a small abstraction layer / pre processor for this. It basically:

  • Adds the 'extends' keyword to templates.
  • Allows overriding 'define' calls (thus default values for greggory are possible)
  • Allows non defined 'template' calls, they just give an empty string
  • Sets the default value of . in 'template' calls to . of the parent

https://github.com/daemonl/go_sweetpl

Defrost answered 3/2, 2014 at 7:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.