假设我有一个带有嵌套子模板的模板,像这样。 playground link
package main
import (
"os"
"text/template"
)
type Person struct {
FirstName string
SecondName string
}
type Document struct {
DocName string
People []Person
}
const document = `
Document name: {{.DocName}}
{{range $person:=.People}}
{{template "person" $person}}
{{end}}
{{- define "person"}}
Person name is: {{.FirstName}} {{.SecondName}}
{{end}}
`
func main() {
d := Document{
DocName: "first try",
People: []Person{
{"Brian", "Kernighan"},
{"Dennis", "Ritchie"},
},
}
t := template.Must(template.New("document").Parse(document))
err := t.Execute(os.Stdout, d)
if err != nil {
panic(err)
}
}
都可以,但是现在我想设置一些文档范围的变量,以更改所有模板及其子模板的行为。像这样(不起作用,恐慌)。 playground link
type Person struct {
FirstName string
SecondName string
}
type Document struct {
DocName string
People []Person
SwitchNameOrder bool
}
const document = `
Document name: {{.DocName}}
{{range $person:=.People}}
{{template "person" $person}}
{{end}}
{{- define "person"}}
{{if $.SwitchNameOrder}} // <---- panic here
Person name is: {{.SecondName}} {{.FirstName}}
{{else}}
Person name is: {{.FirstName}} {{.SecondName}}
{{end}}
{{end}}
`
如何做?有可能吗?
您可以做的一件事是使用模板函数将传递到子模板的变量与父模板中的变量“合并”。
type Person struct {
FirstName string
SecondName string
}
type Document struct {
DocName string
People []Person
SwitchNameOrder bool
}
func personWithDocument(p Person, d Document) interface{} {
return struct {
Person
Document Document
}{p, d}
}
t := template.Must(template.New("document").Funcs(template.FuncMap{
"personWithDocument": personWithDocument,
}).Parse(document))
然后在模板中,您将要做:
const document = `
Document name: {{.DocName}}
{{range $person:=.People}}
{{template "person" (personWithDocument $person $) }}
{{end}}
{{- define "person"}}
{{if .Document.SwitchNameOrder}}
Person name is: {{.SecondName}} {{.FirstName}}
{{else}}
Person name is: {{.FirstName}} {{.SecondName}}
{{end}}
{{end}}
`
我最终要做的是添加一个单独的struct Config并将其复制到各处。也就是说,
type Config struct {
SwitchNameOrder bool
}
type Person struct {
FirstName string
SecondName string
Config Config
// this could also be a pointer,
// but I don't want to deal with nils, so let's copy
}
type Document struct {
DocName string
People []Person
Config Config
}
和
c := Config{SwitchNameOrder: true}
d.Config = c
for _, p := range d.People {
p.Config = c
}
然后在模板中使用它
{{- define "person"}}
{{if .Config.SwitchNameOrder}}
Person name is: {{.SecondName}} {{.FirstName}}
{{else}}
Person name is: {{.FirstName}} {{.SecondName}}
{{end}}
{{end}}
很丑,但是该怎么办
对上述复杂解决方案的一个更好的解决方案是停止尝试使用顶级config选项,而是将其写为模板函数。
{{- define "person"}}
Person name is: {{SwitchNames .FirstName .SecondName}}
{{end}}
和
t := template.Must(template.New("document").Funcs(template.FuncMap{
"SwitchNames": func(first, second string) string {
if switchNames { // variable sits in closure
return second + " " + first
}
return first + " " + second
},
}).Parse(document))