【问题标题】:How to use a field of struct or variable value as template name?如何使用结构或变量值的字段作为模板名称?
【发布时间】:2015-05-04 00:42:57
【问题描述】:

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

如何通过变量值{{template .TemplateName}} 加载模板。还是不可能?

【问题讨论】:

    标签: templates go go-templates


    【解决方案1】:

    很遗憾你不能。

    {{template}} 操作的语法:

    {{template "name"}}
        The template with the specified name is executed with nil data.
    
    {{template "name" pipeline}}
        The template with the specified name is executed with dot set
        to the value of the pipeline.
    

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

    如果允许的语法是:

    {{template pipeline}}
    

    那么你可以使用{{template .TemplName}} 之类的东西,但由于语法只允许使用常量字符串,所以你不能。

    Rob 的推理为什么不允许动态模板调用 (source):

    我们希望模板语言是可静态分析的,因此模板调用的上下文是清晰的、可检查的和可锁定的。如果调用点是完全动态的,则无法做到这一点。类似地,如果一个模板可以属于多个集合,那么它的上下文在集合之间可能会有所不同,这需要同时分析所有集合。由于如果您愿意,这两个约束都很容易解决,但代价是在更高级别的包中丢失了这些静态检查,控制基本模板实现中的情况似乎是明智的。如果约束明确,则更高级别的包(例如假设的仅 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>
    Now I will include template with name: {{.Name}}
    {{.Value}}
    </body>/html>`
    
    const t1 = `I'm template <b>t1</b>.`
    

    输出:

    <html><body>
    Now I will include template with name: t1
    I'm template <b>t1</b>.
    </body>/html>
    

    在Go Playground 上试试。

    模板t1 的结果未转义插入。如果你漏掉template.HTML:

    params.Value = b.String()
    

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

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

    备选方案 #2:重构模板

    您可以重组您的模板,避免在您希望包含具有不同名称的模板的情况下。

    示例:您可能希望创建具有 page 模板的页面,如下所示:

    <html><body>
        Title, headers etc.
        {{template .Page}}
        Footers
    </body></html>
    

    你可以把它改成这样:

    header模板:

    <html><body>
        Title, headers, etc.
    

    footer模板:

        Footers
    </body></html
    

    您的页面模板将包括 header 和 footer,如下所示:

    {{template "header" .}}
        Page content comes here.
    {{template "footer" .}}
    

    备选方案 #3:使用 {{if}} 操作和预定义名称

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

    {{if eq .Name "page1"}}
    
        {{template "page1" .}}
    
    {{else if eq .Name "page2"}}
    
        {{template "page2" .}}
        ...
    
    {{end}}
    

    备选方案#4:修改静态模板文本

    这里的想法是您可以手动修改外部模板的静态文本并插入要包含的内部模板的名称。

    这种方法的缺点是插入内部模板的名称后,必须重新解析模板,所以不推荐这样做。

    【讨论】:

    • 谢谢,@icza。真的,我现在很恐慌:)。我认为存在简单的方法。您的变体非常好,但在我的情况下,我更喜欢像这样使用{{ if .IsArticle }}{{ template "article" . }}{{ else }}{{ template "page" .}}{{ end }}。其中IsArticle bool 标志基于用户请求(URL)。
    • @user4611478 如果您事先知道模板名称并且这不是一个详尽的列表,这也是一个很好的选择。如果您不介意,我将其包含在答案中。
    • 谢谢,两年后我问自己如何处理这种情况,看来使用一堆{{ if }} 真的是要走的路……
    猜你喜欢
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-09
    • 2014-01-17
    • 1970-01-01
    • 2021-03-10
    相关资源
    最近更新 更多