Go template functions

12 November 2024

Recently I wanted to add a function to the data object for a Go template which would allow me to generate some parts dynamically.

Here's what my first attempt looked like. For a template like this:

<p>The number is {{.Number}}</p>
<p>{{.CreateGreeting "Dave"}}</p>

I tried to use it like this:

type Data struct {
	Number int
}

func (d *Data) CreateGreeting(name string) template.HTML {
	return template.HTML("Hello, <b>" + name + "</b>")
}

func main() {
	tmpl := template.Must(template.ParseFiles("template.gotmpl"))
	data := Data{
		Number: 4,
	}

	var buff bytes.Buffer
	if err := tmpl.Execute(&buff, data); err != nil {
		log.Fatal(err)
	}
	fmt.Println(buff.String())
}

Template execution failed with an error, which was a little surprising to me as a Go newbie. The CreateGreeting function is right there!

template: template.gotmpl:2:5: executing "template.gotmpl" at <.CreateGreeting>: can't evaluate field CreateGreeting in type main.Data

The issue at hand is that I've used a pointer receiver for my Data object but I have passed it by value into the template. One possible fix is to pass the data by value to the helper function.

func (d Data) CreateGreeting(name string) template.HTML { // <-- "d Data", no pointer
	return template.HTML("Hello, <b>" + name + "</b>")
}

If I want to avoid the value receiver however, it turns out another possibility is to simply pass in a pointer in the first place.

	if err := tmpl.Execute(&buff, &data); err != nil {  // <-- now using &data
		log.Fatal(err)
	}

This also gives me the output I expect:

<p>The number is 4</p>
<p>Hello, <b>Dave</b></p>