是否有可能在 Go 中使用标准库嵌套模板?

如何在 python 运行时中获得像金贾那样的嵌套模板。我的意思是我如何让一堆模板继承自一个基本模板,只是像金贾/django-template 那样将基本模板块归档。是否有可能在标准库中仅使用 html/template

如果这不可能,我还有什么选择。胡子似乎是一个选择,但我会错过这些美好的微妙的功能,如 html/template的上下文敏感的转义等?还有别的选择吗?

(环境: 谷歌应用程序引擎,Go 运行时 v1,开发-Mac OSx 狮子)

谢谢你的阅读。

30705 次浏览

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.

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

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

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)
}

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)