在Go语言中,使用标准库是否可以有嵌套模板?

huangapple go评论99阅读模式
英文:

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

问题

如何在Python运行时中获得像Jinja那样的嵌套模板。我指的是如何让一堆模板继承自一个基础模板,并填充基础模板的块,就像Jinja/django-templates那样。是否可以只使用标准库中的html/template来实现这个功能。

如果这不可能,那么我的其他选择是什么?Mustache似乎是一个选项,但我会错过html/template这样的一些好用的细微功能,比如上下文敏感的转义等。还有其他的选择吗?

(环境:Google App Engine,Go运行时v1,开发环境 - Mac OS X Lion)

谢谢阅读。

英文:

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.

答案1

得分: 153

是的,这是可能的。html.Template实际上是一组模板文件。如果在这个集合中执行一个定义的块,它可以访问在这个集合中定义的所有其他块。

如果你自己创建一个这样的模板集合的映射,你基本上拥有了Jinja / Django提供的相同的灵活性。唯一的区别是html/template包没有直接访问文件系统的权限,所以你必须自己解析和组合模板。

考虑以下示例,其中有两个不同的页面("index.html"和"other.html"),它们都继承自"base.html":

// base.html的内容:
{{define "base"}}<html>
  <head>{{template "head" .}}</head>
  <body>{{template "body" .}}</body>
</html>{{end}}

// index.html的内容:
{{define "head"}}<title>index</title>{{end}}
{{define "body"}}index{{end}}

// other.html的内容:
{{define "head"}}<title>other</title>{{end}}
{{define "body"}}other{{end}}

以及以下模板集合的映射:

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

现在你可以通过调用以下方式渲染你的"index.html"页面:

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

并且你可以通过调用以下方式渲染你的"other.html"页面:

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

通过一些技巧(例如一致的模板文件命名约定),甚至可以自动生成tmpl映射。

英文:

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 &quot;base&quot;}}&lt;html&gt;
  &lt;head&gt;{{template &quot;head&quot; .}}&lt;/head&gt;
  &lt;body&gt;{{template &quot;body&quot; .}}&lt;/body&gt;
&lt;/html&gt;{{end}}

// Content of index.html:
{{define &quot;head&quot;}}&lt;title&gt;index&lt;/title&gt;{{end}}
{{define &quot;body&quot;}}index{{end}}

// Content of other.html:
{{define &quot;head&quot;}}&lt;title&gt;other&lt;/title&gt;{{end}}
{{define &quot;body&quot;}}other{{end}}

And the following map of template sets:

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

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

tmpl[&quot;index.html&quot;].Execute(&quot;base&quot;, data)

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

tmpl[&quot;other.html&quot;].Execute(&quot;base&quot;, data)

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

答案2

得分: 14

注意,当执行基础模板时,您必须向子模板传递值,这里我只是简单地传递“。”,以便将所有内容传递下去。

模板一显示{{.}}

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

模板二显示{{.domains}},这是传递给父模板的。

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

注意,如果我们使用{{template "content"}}而不是{{template "content" .}},则无法从内容模板中访问.domains。

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

英文:

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 &quot;base&quot;}}
&lt;html&gt;
        &lt;div class=&quot;container&quot;&gt;
            {{.}}
            {{template &quot;content&quot; .}}
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
{{end}}

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

{{define &quot;content&quot;}}
{{.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[&quot;domains&quot;] = domains.Domains
	if err := groupsTemplate.ExecuteTemplate(w, &quot;base&quot;, DomainsData); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

答案3

得分: 6

在使用其他模板包之后,现在我主要使用标准的html/template包,我想我当时太天真了,没有意识到它提供的简单性和其他好处。我使用了与接受的答案非常相似的方法,但有以下更改:

你不需要用额外的base模板包装你的布局,每个解析的文件都会创建一个模板块,所以在这种情况下是多余的。我还喜欢使用新版本的go中提供的block操作,它允许你在子模板中没有提供默认块内容时使用默认内容。

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

你的页面模板可以是相同的

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

现在要执行模板,你需要这样调用

tmpl["index.html"].ExecuteTemplate(os.Stdout, "base.html", data)
英文:

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
&lt;head&gt;{{block &quot;head&quot; .}} Default Title {{end}}&lt;/head&gt;
&lt;body&gt;{{block &quot;body&quot; .}} default body {{end}}&lt;/body&gt;

and you page templates can be the same as

// Content of index.html:
{{define &quot;head&quot;}}&lt;title&gt;index&lt;/title&gt;{{end}}
{{define &quot;body&quot;}}index{{end}}

// Content of other.html:
{{define &quot;head&quot;}}&lt;title&gt;other&lt;/title&gt;{{end}}
{{define &quot;body&quot;}}other{{end}}

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

tmpl[&quot;index.html&quot;].ExecuteTemplate(os.Stdout, &quot;base.html&quot;, data)

答案4

得分: 4

使用Pongo,它是Go模板的超集,支持{{extends}}和{{block}}标签用于模板继承,就像Django一样。

英文:

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

答案5

得分: 4

我已经花了几天时间回答这个问题,最后决定写一个小的抽象层/预处理器来处理这个问题。它基本上做了以下几点:

  • 添加了“extends”关键字到模板中。
  • 允许覆盖“define”调用(因此可以设置greggory的默认值)。
  • 允许未定义的“template”调用,它们只会返回一个空字符串。
  • 将“template”调用中的默认值设置为父模板中的值。

https://github.com/daemonl/go_sweetpl

英文:

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

huangapple
  • 本文由 发表于 2012年7月13日 17:28:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/11467731.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定