Composing Golang templates dynamically with addParseTree
In the Golang ecosystem, it's only a matter of time before you hit an open source project (such as Helm, Hugo, etc.) that relies on the standard template package.
If you want to be able to compose templates dynamically, you'll want to look into using addParseTree
.
Say you're working with a couple of templates and you want to create a parent template that uses both of them:
func main() {
// Ignoring errors for this example
template1, _ := template.New("template1").Parse("Template 1: {{ .Name }}")
template2, _ := template.New("template2").Parse("Template 2: {{ .Age }}")
// Refers to template1 and template2
template3, _ := template.New("template3").Parse(`{{ template "template1" .}}{{ template "template2" .}}`)
// Will fail
template3.Execute(...)
}
In the creation of template3
, we want a way of letting it know what template1
and template2
should refer to or else you'll get the following error when you try to run the .Execute
method on it:
panic: template: template3:1:11: executing "template3" at <{{template "template1" ....>: template "template1" not defined
This is where addParseTree
comes in.
Behind a template, there's an embedded struct (*parse.Tree
) you can refer representing a tree structure of the parsed template. In a parent template, you can use the addParseTree
method to define a child template:
func main() {
template1, err := template.New("template1").Parse("Template 1: {{ .Name }}")
must(err)
template2, err := template.New("template2").Parse("Template 2: {{ .Age }}")
must(err)
template3, err := template.New("template3").Parse(`{{ template "template1" .}}{{ template "template2" .}}`)
must(err)
_, err = template3.addParseTree(template1.Name(), template1.Tree)
must(err)
_, err = template3.addParseTree(template2.Name(), template2.Tree)
must(err)
// Will work now
template3.Execute(...)
}
You should now be able to successfully .Execute
the parent template. You can even write a helper function if you need to do this repeatedly:
func addChildTemplate(parent *template.Template, child *template.Template) (*template.Template, error) {
return parent.addParseTree(child.Name(), child.Tree)
}