Templates 04 - Adicionando funções

Adicionando funções

Em algumas situações podemos precisar de funções que não existem no pacote template, para essas situações podemos mapear funções para dentro do template.

O primeiro exemplo será com a função Repeat do pacote strings

A função Repeat recebe dois parâmetros, o primeiro é uma string que será repetida, e o segundo é um inteiro que determina quantas vezes aquela string deverá ser repetida, exemplo:

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

Podemos utilizar a função repeat dentro dos nossos templates da seguinte forma:

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

Nós temos que mapear as funções dessa forma:

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

E quando vamos parsear o arquivos nós vamos injetar as funções através da função Funcs

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

Com as funções mapeadas para nosso template, já podemos utilizá-la:

Template addf.tmpl

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

Essa é a saída do nosso programa:

Sales
----------

Mapeamos a função Repeat com o nome repeat dentro do template.

Mas poderíamos ter mapeado a função Repeat como func01 por exemplo:

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

Podemos criar a nossa própria função e mapeá-la para o template, exemplo:

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" "!!!!!" }}

Esse é a saída do programa:

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

Podemos utilizar os próprios campos através dos dados enviados para o template como entrada para as funções:

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

Repare que acessamos o campo .ReportDivider com o símbolo $, isso é porque naquele ponto estamos dentro do range e queremos acessar um campo que está acima do slice, dessa forma conseguimos acessar o escopo principal.

Essa é a saída do programa:

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