Templates 01 - Básico

Templates

Go fornece um poderoso sistema de templates através dos pacotes text/template and html/template, habilitando a criação de
de saídas de texto e HTML dinâmicos. Enquanto text/template é utilizado para texto puro, html/template é desenhando
especificamente para aplicações web, oferecendo securança adicional contra ataques de injeção.

Templates em Go usam uma abordagem declarativa onde marcadores, definidos por {{ }}, são substituídos por dados durante o tempo de execução.

Esse recurso é amplamente utilizado para a geração de emails, relatórios, arquivos de configuração e páginas web dinâmicas.

Entendendo o conceito de templates

Esse é um dos exemplos mais simples de templates que podemos criar, mas nele está uma informação muito importante de como os dados são enviados para dentro do template.

func main() {
	var tmplFile = "{{ . }}"
	tmpl, err := template.New("myFile").Parse(tmplFile)
	if err != nil {
		panic(err)
	}

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

Templates mais complexos podem ficar dentro de arquivos de texto, mas no nosso exemplo esse é o nosso template:

var tmplFile = "{{ . }}"

Tudo que estiver dentro dos marcadores {{ }} vai ser interpretado em tempo de execução.

tmpl, err := template.New("myFile").Parse(tmplFile)
if err != nil {
    panic(err)
}

Aqui a variável tmpl foi definida como um novo template, myFile é somente o nome do template, e nesse exemplo não fará nenhuma diferença o nome utilizado.

Em seguida pedimos que um parse seja executado com o conteúdo da variável tmplFile.

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

Aqui pedimos que o template seja executado, definimos a saída dele que pode ser qualquer destino que implemente a interface io.Writer, e os dados que nesse caso é somente a string Goku

O valor passado dentro do Execute é acessado pelo marcador {{ . }}

Dessa forma a saída da execução do nosso template é > Goku

Utilizando structs com templates

Da mesma forma que passamos uma string para o template ser executado, podemos passar uma struct e acessar os campos dessa struct dentro do template.

type User struct {
	Name     string
	LastName string
	Country  string
}

func main() {
	var tmplFile = `{{ .Name }} {{ .LastName }} {{ .Country }}`

	user := User{"John", "Doe", "USA"}

	tmpl, err := template.New("myFile").Parse(tmplFile)
	if err != nil {
		panic(err)
	}

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

No exemplo acima estamos utilizando os valores da struct dentro do template com os marcadores {{ .Name }} {{ .LastName }} {{ .Country }}

Dessa forma, a saída do programa acima será: > John Doe USA

Utilizando slices

Em um cenário mais realístico, vamos precisar trabalhar com slices, dessa forma poderemos passar vários valores para serem inseridos dentro do template.

Para esse exemplo vamos criar um arquivo onde ficará o template, o nome do arquivo será template.tmpl

Esse é o conteúdo do arquivo template.tmpl

{{ range . }}
----------
Name: {{ .Name }} LastName: {{ .LastName }} Country: {{ .Country }}
{{ end }}

A diferença do exemplo anterior é que nós agora vamos passar um slice para a função que executa o template.

type User struct {
	Name     string
	LastName string
	Country  string
}

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

	users := []User{
		{"John", "Doe", "USA"},
		{"Gavin", "Steele", "USA"},
		{"Ashton", "Walsh", "CA"},
	}

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

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

A função range dentro dos marcadores irá percorrer todo o slice, e poderemos acessar os campos da struct da mesma maneira como fizemos no exemplo anterior.

Note que o marcador {{ range . }} precisa ser fechado com o marcador {{ end }}

Essa será a saída do nosso programa:

----------
Name: John LastName: Doe Country: USA

----------
Name: Gavin LastName: Steele Country: USA

----------
Name: Ashton LastName: Walsh Country: CA

Repare que há algumas quebras de linha no texto, mas podemos evitar isso adicionando o sinal de menos - ao marcador {{ range -}}

Com o sinal de menos:

{{ range . -}}
----------
Name: {{ .Name }} LastName: {{ .LastName }} Country: {{ .Country }}
{{ end }}

Esse será o resultado do nosso programa:

----------
Name: John LastName: Doe Country: USA
----------
Name: Gavin LastName: Steele Country: USA
----------
Name: Ashton LastName: Walsh Country: CA