2013-12-21 19 views
6

我没有看到使用动态名称调用模板(文本或html)的方法。例如:使用动态名称调用其他模板

这工作:

{{template "Blah" .}} 

这个错误有 “意外 ”$ BlahVar“ 模板调用”:

{{$BlahVar := "Blah"}} 
{{template $BlahVar .}} 

我试图解决的整体问题我需要基于配置文件有条件地呈现模板 - 所以我不知道提前模板的名称。很明显,我可以在FuncMap中放置一个函数,它只是执行一个单独的模板解析和调用,并返回结果,但想知道是否有更好的方法。

+0

@Boushley gotcha。看到我刚刚发布的其他答案,它提供了我最终使用的解决方案。 –

+1

在此期间得到了答案? – Kiril

+0

我也在寻找一种办法... – Creasixtine

回答

0

一种不同的方法,一个有才华的开发我想出了工作是进行后处理的模板实例找到任何模板包括没有被定义并期待匹配文件的文件系统并为每个找到的文件系统解析它;然后渲染。

这给你一个设置类似如下:

的意见/ index.html的:

{{template "/includes/page-wrapper.html" .}} 

{{define "body"}} 
<div>Page guts go here</div> 
{{end}} 

{{define "head_section"}} 
<title>Title Tag</title> 
{{end}} 

包括/页wrapper.html:

<html> 
<head> 
{{block "head_section" .}}{{end}} 
<head> 
<body> 

{{template "body" .}} 

</body> 
</html> 

而且你ServeHTTP()方法查找“views”目录中的文件,加载并解析它,然后调用TmplIncludeAll()(如下)。

我最终将这个基本概念作为几个函数进行了调整,如下所示。 t是分析之后但呈现之前的模板。而fs是“视图”和“包含”直播的目录(参见上文)。

func TmplIncludeAll(fs http.FileSystem, t *template.Template) error { 

    tlist := t.Templates() 
    for _, et := range tlist { 
     if et != nil && et.Tree != nil && et.Tree.Root != nil { 
      err := TmplIncludeNode(fs, et, et.Tree.Root) 
      if err != nil { 
       return err 
      } 
     } 
    } 

    return nil 
} 

func TmplIncludeNode(fs http.FileSystem, t *template.Template, node parse.Node) error { 

    if node == nil { 
     return nil 
    } 

    switch node := node.(type) { 

    case *parse.TemplateNode: 
     if node == nil { 
      return nil 
     } 

     // if template is already defined, do nothing 
     tlist := t.Templates() 
     for _, et := range tlist { 
      if node.Name == et.Name() { 
       return nil 
      } 
     } 

     t2 := t.New(node.Name) 

     f, err := fs.Open(node.Name) 
     if err != nil { 
      return err 
     } 
     defer f.Close() 

     b, err := ioutil.ReadAll(f) 
     if err != nil { 
      return err 
     } 

     _, err = t2.Parse(string(b)) 
     if err != nil { 
      return err 
     } 

     // start over again, will stop recursing when there are no more templates to include 
     return TmplIncludeAll(fs, t) 

    case *parse.ListNode: 

     if node == nil { 
      return nil 
     } 

     for _, node := range node.Nodes { 
      err := TmplIncludeNode(fs, t, node) 
      if err != nil { 
       return err 
      } 
     } 

    case *parse.IfNode: 
     if err := TmplIncludeNode(fs, t, node.BranchNode.List); err != nil { 
      return err 
     } 
     if err := TmplIncludeNode(fs, t, node.BranchNode.ElseList); err != nil { 
      return err 
     } 

    case *parse.RangeNode: 
     if err := TmplIncludeNode(fs, t, node.BranchNode.List); err != nil { 
      return err 
     } 
     if err := TmplIncludeNode(fs, t, node.BranchNode.ElseList); err != nil { 
      return err 
     } 

    case *parse.WithNode: 
     if err := TmplIncludeNode(fs, t, node.BranchNode.List); err != nil { 
      return err 
     } 
     if err := TmplIncludeNode(fs, t, node.BranchNode.ElseList); err != nil { 
      return err 
     } 

    } 

    return nil 
} 

这是我最喜欢的方法,我已经使用了一段时间了。它的优点是只有一个模板渲染,错误信息很好,干净,Go模板标记非常易读和明显。如果HTML /模板的胆量会很大。模板使这个实现起来更简单,但它总体上是一个很好的解决方案IMO。

6

另一种方式,虽然也许不是一个更好的方法,但将有单独的模板文件都提供相同的命名模板。例如,假设你有一个网页共享布局:

<html> 
    ... 
    <body> 
    {{template "body" .}} 
    </body> 
</html> 

在每一页你这样做:

{{define "body"}} 
    This will be in the body 
{{end}} 

然后合并他们在代码:

func compileTemplate(layout, name string) (*template.Template, error) { 
    tpl := template.New(name) 
    tpl, err := tpl.ParseFiles(
     "views/layouts/"+layout+".htm", 
     "views/"+name+".htm", 
    ) 
    if err != nil { 
     return nil, err 
    } 
    return tpl, nil 
} 
+0

有趣 - 并没有特别解决我的问题,但是这是另一种在某些情况下可能有用的方法。不过谢谢。 –

7

作为注意到这一点,为了跟进,我最终得出了这个问题的两个主要答案:1)尽量避免这种情况。在几种情况下,简单的if语句运行良好。 2)我能够使用FuncMap中的一个函数来完成这个工作,它只是做一个单独的渲染。这不是世界上最伟大的事情,但它确实有效并解决了这个问题。这里是一个完全独立的演示,显示了主意:

package main 

import (
    "bytes" 
    "html/template" 
    "os" 
) 

func main() { 

    var err error 

    // our main template here calls a sub template 
    tpl := template.New("main") 

    // provide a func in the FuncMap which can access tpl to be able to look up templates 
    tpl.Funcs(map[string]interface{}{ 
     "CallTemplate": func(name string, data interface{}) (ret template.HTML, err error) { 
      buf := bytes.NewBuffer([]byte{}) 
      err = tpl.ExecuteTemplate(buf, name, data) 
      ret = template.HTML(buf.String()) 
      return 
     }, 
    }) 

    // this is the main template 
    _, err = tpl.Parse(` 

{{$Name := "examplesubtpl"}} 

from main template 

{{CallTemplate $Name .}} 

`) 
    if err != nil { 
     panic(err) 
    } 

    // whatever code to dynamically figure out what templates to load 

    // a stub just to demonstrate 
    _, err = tpl.New("examplesubtpl").Parse(` 

this is from examplesubtpl - see, it worked! 

`) 
    if err != nil { 
     panic(err) 
    } 

    err = tpl.Execute(os.Stdout, map[string]interface{}{}) 
    if err != nil { 
     panic(err) 
    } 

}