Templates 04 - Adding functions

Adding functions

In some situations, we might need functions that do not exist in the template package. For these cases, we can map functions into the template.

The first example will use the Repeat function from the strings package.

The Repeat function takes two parameters: the first is a string to be repeated, and the second is an integer that determines how many times that string should be repeated. For example:

func main() {
	fmt.Print(strings.Repeat("-", 10))
}
----------

We can use the repeat function inside our templates as follows:

type Report struct {
	ReportName    string
	ReportYear    string
	ReportUser    string
	ReportDivider string
	Users         []User
}

type User struct {
	Name              string
	LastName          string
	Country           string
	Admin             bool
	YearsOfExperience int
}

func main() {
	tmplFile := "addf.tmpl"

	funcMap := template.FuncMap{
		"repeat": strings.Repeat,
	}

	users := []User{
		{"John", "Doe", "USA", true, 25},
		{"Gavin", "Steele", "USA", false, 3},
		{"Ashton", "Walsh", "CA", false, 5},
	}

	report := Report{
		ReportName:    "Sales",
		ReportYear:    "2025",
		ReportUser:    "Goku",
		ReportDivider: "*",
		Users:         users,
	}

	tmpl, err := template.New(tmplFile).Funcs(funcMap).ParseFiles(tmplFile)
	if err != nil {
		panic(err)
	}

	err = tmpl.Execute(os.Stdout, report)
	if err != nil {
		panic(err)
	}
}

We need to map the functions like this:

funcMap := template.FuncMap{
    "repeat": strings.Repeat,
}

And when parsing the files, we will inject the functions using the Funcs function.

tmpl, err := template.New(tmplFile).Funcs(funcMap).ParseFiles(tmplFile)

With the functions mapped to our template, we can now use them:

Template addf.tmpl

{{ .ReportName }}
{{ repeat "-" 10 }}

This is the output of our program:

Sales
----------

We mapped the Repeat function with the name repeat inside the template.

But we could have mapped the Repeat function as func01, for example:

funcMap := template.FuncMap{
    "func01": strings.Repeat,
}
{{ .ReportName }}
{{ func01 "-" 10 }}

We can create our own function and map it to the template, for example:

func Greetings(name, prefix, suffix string) string {
	return fmt.Sprintf("%s %s %s", prefix, name, suffix)
}

func main() {
	tmplFile := "addf.tmpl"

	funcMap := template.FuncMap{
		"repeat":    strings.Repeat,
		"greetings": Greetings,
	}

	users := []User{
		{"John", "Doe", "USA", true, 25},
		{"Gavin", "Steele", "USA", false, 3},
		{"Ashton", "Walsh", "CA", false, 5},
	}

	report := Report{
		ReportName:    "Sales",
		ReportYear:    "2025",
		ReportUser:    "Goku",
		ReportDivider: "*",
		Users:         users,
	}

	tmpl, err := template.New(tmplFile).Funcs(funcMap).ParseFiles(tmplFile)
	if err != nil {
		panic(err)
	}

	err = tmpl.Execute(os.Stdout, report)
	if err != nil {
		panic(err)
	}
}
{{ .ReportName }}
{{ repeat "-" 10 }}
{{ greetings "Goku" "Hello" "!!!!!" }}

This is the program’s output:

Sales
----------
Hello Goku !!!!!

We can use the fields themselves through the data sent to the template as input for the functions:

{{ .ReportName }}{{ "\n" }}
{{- range .Users -}}
{{ repeat $.ReportDivider 10 }}{{ "\n" }}
{{- greetings .Name "Hello" "!!!!!" }}{{ "\n" }}
{{- end -}}

Notice that we access the .ReportDivider field with the $ symbol. This is because at that point we are inside the range and want to access a field that is above the slice. This way, we can access the main scope.

This is the program’s output:

Sales
**********
Hello John !!!!!
**********
Hello Gavin !!!!!
**********
Hello Ashton !!!!!