Templates 01 - Basics

Templates

Go provides a powerful template system through the text/template and html/template packages, enabling the creation of dynamic text and HTML outputs. While text/template is used for plain text generation, html/template is specifically designed for web applications, offering additional security against injection attacks.

Templates in Go use a declarative approach where placeholders, defined using {{ }}, are replaced with data at runtime.

This feature is widely utilized for generating emails, reports, configuration files, and dynamic web pages.

Understanding the concept of templates

This is one of the simplest examples of templates we can create, but it contains very important information about how data is sent to the 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)
	}
}

More complex templates can be stored inside text files, but in our example this is our template:

var tmplFile = "{{ . }}"

Everything inside the {{ }} tags will be interpreted at runtime.

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

Here the tmpl variable was defined as a new template, myFile is just the name of the template, and in this example it will not make any difference which name is used.

Next we ask that a parse be performed on the contents of the tmplFile variable.

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

Here we ask the template to be executed, we define its output which can be any destination that implements the io.Writer interface, and the data which in this case is just the string Goku

The value passed inside Execute is accessed by the {{ . }} marker

This way the output of our template execution is > Goku

Using structs with templates

In the same way that we pass a string for the template to be executed, we can pass a struct and access the fields of this struct within the 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)
	}
}

In the example above we are using the values of the struct inside the template with the markers {{ .Name }} {{ .LastName }} {{ .Country }}

Thus, the output of the above program will be: > John Doe USA

Using slices

In a more realistic scenario, we will need to work with slices, this way we can pass several values to be inserted into the template.

For this example we will create a file where the template will be, the name of the file will be template.tmpl

This is the content of the file template.tmpl

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

The difference from the previous example is that we will now pass a slice to the function that executes the 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)
	}
}

The range function inside the markers will go through the entire slice, and we will be able to access the fields of the struct in the same way as we did in the previous example.

Note that the {{ range . }} marker needs to be closed with the {{ end }} marker

This will be the output of our program:

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

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

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

Note that there are some line breaks in the text, but we can avoid this by adding the minus sign - to the {{ range -}} marker.

With the minus sign:

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

This will be the result of our program:

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