如何将结构体或变量值的字段用作模板名称?

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

How to use a field of struct or variable value as template name?

问题

我们可以通过{{define "home"}}来定义模板名称,然后通过{{template "home"}}在其他(父级)模板中加载它。

我如何通过变量值{{template .TemplateName}}来加载模板?或者这是不可能的吗?

英文:

We can define template name via {{define "home"}}, and then load it in other (parent) template via {{template "home"}}.

How I can load template via variable value {{template .TemplateName}}. Or it's impossible?

答案1

得分: 28

很抱歉,你不能这样做。

{{template}} 动作的语法如下:

{{template "name"}}
    使用空数据执行指定名称的模板。

{{template "name" pipeline}}
    使用 pipeline 的值将 dot 设置为指定名称的模板。

要包含的模板名称是一个常量字符串,而不是根据参数在执行过程中可能变化的 pipeline。

如果允许的语法是:

{{template pipeline}}

那么你可以使用类似 {{template .TemplName}} 的方式,但由于语法只允许常量字符串,所以你不能这样做。

Rob 解释为什么不允许动态模板调用的原因(来源):

> 我们希望模板语言是静态可分析的,这样模板调用的上下文就清晰、可检查和可锁定。如果调用点完全是动态的,这是做不到的。同样,如果一个模板可以属于多个集合,它的上下文在不同集合之间可能不同,这将需要同时分析所有集合。由于如果你愿意,这两个约束都很容易绕过,只是在更高级的包中失去了那些静态检查,所以在基本模板实现中控制这种情况似乎是明智的。如果约束清晰,更高级的包(比如一个假设的仅限于 HTML 的包装器)可以更容易地保证没有绕过。

替代方案1:先执行可包含的模板

你可以先执行你想要包含的模板,然后将结果插入到你想要包含的位置。在插入时,你可以使用特殊类型来避免内部模板结果的转义,例如在 HTML 模板中使用 html.HTML

看下面的例子:

func main() {
    t := template.Must(template.New("t").Parse(t))
    template.Must(t.New("t1").Parse(t1))

    params := struct {
        Name  string
        Value interface{}
    }{"t1", nil}
    b := bytes.Buffer{}
    t.ExecuteTemplate(&b, params.Name, nil)
    params.Value = template.HTML(b.String())

    t.Execute(os.Stdout, params)
}

const t = `<html><body>
现在我将包含名称为:{{.Name}} 的模板
{{.Value}}
</body></html>`

const t1 = `我是模板 <b>t1</b>。`

输出:

<html><body>
现在我将包含名称为:t1 的模板
我是模板 <b>t1</b>。
</body></html>

你可以在 Go Playground 上尝试运行。

模板 t1 的结果被插入到了模板中,没有进行转义。如果你不使用 template.HTML

params.Value = b.String()

t1 将被转义插入,如下所示:

<html><body>
现在我将包含名称为:t1 的模板
我&amp;#39;m template &amp;lt;b&amp;gt;t1&amp;lt;/b&amp;gt;.
</body></html>

替代方案2:重构模板

你可以重构你的模板,避免需要包含具有不同名称的模板的情况。

例如,你可能想要创建包含 page 模板的页面,类似于以下内容:

<html><body>
    标题、头部等内容
    {{template .Page}}
    底部
</body></html>

你可以将其重构为以下内容:

header 模板:

<html><body>
    标题、头部等内容

footer 模板:

    底部
</body></html>

然后,你的页面模板可以像这样包含 headerfooter

{{template "header" .}}
    页面内容在这里。
{{template "footer" .}}

替代方案3:使用 {{if}} 动作和预定义名称

如果你事先知道模板的名称,并且它不是一个穷尽列表,你可以使用 {{if}} 模板动作来包含所需的模板。例如:

{{if eq .Name "page1"}}

    {{template "page1" .}}

{{else if eq .Name "page2"}}

    {{template "page2" .}}
    ...

{{end}}

替代方案4:修改静态模板文本

这个方法的思路是你可以手动修改外部模板的静态文本,并插入你想要包含的内部模板的名称。

这种方法的缺点是,在插入内部模板的名称后,你必须重新解析模板,所以我不建议使用这种方法。

英文:

Unfortunately you can't.

The syntax of the {{template}} action:

{{template &quot;name&quot;}}
	The template with the specified name is executed with nil data.

{{template &quot;name&quot; pipeline}}
	The template with the specified name is executed with dot set
	to the value of the pipeline.

The name of the template to be included is a constant string, it is not a pipeline which could vary during execution based on parameters.

If the allowed syntax would be:

{{template pipeline}}

then you could use something like {{template .TemplName}} but since the syntax only allows a constant string, you can't.

Reasoning from Rob why dynamic template invocation is not allowed (source):

> We want the template language to be statically analyzable so the context of a template's invocation is clear, checkable, and lockdownable. If an invocation point is totally dynamic, this can't be done. Similarly, if a template can belong to multiple sets, its context can differ between sets in a way that would require all sets to be analyzed simultaneously. Since both these constraints are easy to work around if you want to, at the cost of losing those static checks in a higher-level package, it seemed wise to control the situation in the base template implementation. A higher-level package, such as a hypothetical HTML-only wrapper, can guarantee no workarounds more easily if the constraints are clear.

Alternative #1: Execute Includable Template First

What you can do is execute the template you would want to include first, and insert the result where you want to include it. You can use special types not to escape the result of the inner template when inserting, for example html.HTML in case of HTML templates.

See this example:

func main() {
	t := template.Must(template.New(&quot;t&quot;).Parse(t))
	template.Must(t.New(&quot;t1&quot;).Parse(t1))

	params := struct {
		Name  string
		Value interface{}
	}{&quot;t1&quot;, nil}
	b := bytes.Buffer{}
	t.ExecuteTemplate(&amp;b, params.Name, nil)
	params.Value = template.HTML(b.String())

    t.Execute(os.Stdout, params)
}

const t = `&lt;html&gt;&lt;body&gt;
Now I will include template with name: {{.Name}}
{{.Value}}
&lt;/body&gt;/html&gt;`

const t1 = `I&#39;m template &lt;b&gt;t1&lt;/b&gt;.`

Output:

&lt;html&gt;&lt;body&gt;
Now I will include template with name: t1
I&#39;m template &lt;b&gt;t1&lt;/b&gt;.
&lt;/body&gt;/html&gt;

Try it on the Go Playground.

The result of template t1 was inserted unescaped. If you leave out template.HTML:

params.Value = b.String()

t1 would be inserted escaped, like this:

&lt;html&gt;&lt;body&gt;
Now I will include template with name: t1
I&amp;#39;m template &amp;lt;b&amp;gt;t1&amp;lt;/b&amp;gt;.
&lt;/body&gt;/html&gt;

Alternative #2: Restructure Templates

You can restructure your templates not to be in situations where you would want to include a template with varying names.

Example: you might want to create pages where you have a page template something like this:

&lt;html&gt;&lt;body&gt;
    Title, headers etc.
    {{template .Page}}
    Footers
&lt;/body&gt;&lt;/html&gt;

You can restructure it to be something like this:

header template:

&lt;html&gt;&lt;body&gt;
    Title, headers, etc.

footer template:

    Footers
&lt;/body&gt;&lt;/html

And your page templates would include header and footer like this:

{{template &quot;header&quot; .}}
    Page content comes here.
{{template &quot;footer&quot; .}}

Alternative #3: Use {{if}} action and predefined names

If you know the template names prior and it is not an exhausting list, you can use the {{if}} template action to include the desired template. Example:

{{if eq .Name &quot;page1&quot;}}

    {{template &quot;page1&quot; .}}

{{else if eq .Name &quot;page2&quot;}}

    {{template &quot;page2&quot; .}}
    ...

{{end}}

Alternative #4: Modifying the static template text

The idea here is that you could modify the static text of the outer template manually and insert the name of the inner template you want to include.

The downside of this method is that after inserting the name of the inner template, you have to re-parse the template, so I don't recommend this.

huangapple
  • 本文由 发表于 2015年3月3日 19:21:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/28830543.html
匿名

发表评论

匿名网友

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

确定