2013-12-21 20 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) 
    } 

}