我有以下代码:
t, err := template.New("template").Funcs(funcMap).Parse("Howdy {{ myfunc . }}")
在这种形式下一切正常。但是,如果我使用ParseFiles执行完全相同的操作,将上面的文本放在template.html
中则不行:
t, err := template.New("template").Funcs(funcMap).ParseFiles("template.html")
我能够以下面的形式使ParseFiles工作,但无法让Funcs生效:
t, err := template.ParseFiles("template.html")
t.Funcs(funcMap)
当然,最后一种形式是直接调用函数而不是调用接收器方法,所以不一样。
任何人都有任何想法在这里发生了什么?很难在模板中找到很多细节。
试试这个:
var templates = template.Must(template.New("").Funcs(fmap).ParseFiles("1.tmpl, "2.tmpl"))
ParseFiles
应该有一些模板的名称,这是filename的基本名称。但你打电话给template.New
,它会创建一个名为error
的新模板。所以你应该选择一个模板。
package main
import (
"text/template"
"log"
"os"
"strings"
)
func main() {
tmpl, err := template.New("error").Funcs(template.FuncMap{
"trim": strings.TrimSpace,
}).ParseFiles("foo.tmpl")
if err != nil {
log.Fatal(err)
}
tmpl = tmpl.Lookup("foo.tmpl")
err = tmpl.Execute(os.Stdout, " string contains spaces both ")
if err != nil {
log.Fatal(err)
}
}
{{. | trim}}
做了一些挖掘,并在源代码中找到了template.ParseFiles
的评论:
如果尚未定义,则第一个模板将成为返回值,并且我们将该模板用于后续的新调用以将所有模板关联在一起。此外,如果此文件与t同名,则此文件将成为t的内容,因此t,err:= New(name).Funcs(xxx).ParseFiles(name)有效。否则,我们创建一个与t关联的新模板。
因此,根据上面的示例,格式应如下所示:
t, err := template.New("template.html").Funcs(funcMap).ParseFiles("path/template.html")
.New("template.html")
创建一个具有给定名称的空模板,.Funcs(funcMap)
将我们想要应用的任何自定义函数关联到我们的模板,然后.ParseFiles("path/template.html")
解析一个或多个模板,并了解这些函数并将内容与该名称的模板相关联。
请注意,第一个文件的基本名称必须与New
中使用的名称相同。解析的任何内容将与具有该系列中第一个文件的相同基本名称的空预先存在的模板或具有该基本名称的新模板相关联。
因此,在上面的示例中,创建了一个名为“template”的空模板,并且具有与之关联的功能图。然后创建了一个名为“template.html”的新模板。这些都不一样!因为,ParseFiles
被称为最后,t
最终成为“template.html”模板,没有附加任何功能。
最后一个例子怎么样?为什么这不起作用? template.ParseFiles调用接收方法Parse,后者又应用任何以前注册的函数:
trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
这意味着必须在解析之前注册自定义函数。解析模板后添加函数没有任何影响,在尝试调用自定义函数时导致运行时的nil指针错误。
所以我认为这涵盖了我原来的问题。这里的设计似乎有点笨重。没有意义的是,我可以将ParseFiles
链接到一个模板,并最终返回一个不同的模板,而不是我正在链接的模板,如果它们恰好没有命名相同。这是违反直觉的,可能应该在未来的版本中解决,以避免混淆。