mdaverde.com

Edited on February, 12 2018

Dynamic template creation in Go

How to use addParseTree in native Go templates

If you plan on spending time developing in Go, it’s only a matter of time before you hit an open source project (Helm, Hugo, etc.) that uses Go’s native templating package. If you want to be able to create templates out of templates dynamically, you’ll most likely want to look at using addParseTree.

Let’s say you’re working with a couple of templates and you want to create a parent template that uses both of them.

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)
}

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. In the creation of a template, there’s an embedded struct (*parse.Tree) that you can refer to which is a tree representation of the parsed template. In a parent template (in our case, template3) you can use the addParseTree method to add a child template as a defined 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)

  // 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)
}