Dynamic templates with Golang

How to use addParseTree in Golang templates

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

Written by