Go Template Walkthrough Guide

Concepts & Syntax

They’re delimited by {{ }}. Other, other non-delimited parts are left untouched.

Data evaluations

To obtain data from a struct, you can use the {{ .FieldName}} action, which will replace it with the FieldName value of the given struct, on parse time. The struct is given to the Execute function, which we’ll cover later.

There’s also the {{.}} action that you can use to refer to a value of non-struct types.

Conditions

if,else if,end structure is supported. For example:

{{if .FieldName}} Value of FieldName is {{ .FieldName }} {{end}}

Or

{{if .FieldName}} action {{ else }} action 2 {{end}}

Loops

Using the range action you can loop through a slice. A range actions is defined using the {{range .Member}} ... {{end}} template.

If your slice is a non-struct type, you can refer to the value using the {{ . }} action. In case of structs, you can refer to the value using the {{ .Member }} action, as already explained.

Functions, Pipelines and Variables

Actions have several built-in functions that are used along with pipelines to additionally parse output. Pipelines are annotated with | and the default behavior is sending data from the left side to the function to the right side.

Functions are used to escape the action’s result. There’re several functions available by default such as, html which returns HTML escaped output, safe against code injection or js which returns JavaScript escaped output.

Using the with action, you can define variables that are available in that with block: {{ with $x := <^>result-of-some-action<^> }} {{ $x }} {{ end }}.

Parsing Templates

The three most important and most frequently used functions are:

  • New — allocates new, undefined template,

  • Parse — parses given template string and return parsed template,

  • Execute — applies parsed template to the data structure and writes result to the given writer.

Let's run through a sample:

$ mkdir go-template && cd go-template

$ cat > main.go <<EOF
package main

import (
	"os"
	"text/template"
)

type Todo struct {
	Name        string
	Description string
}

func main() {
	td := Todo{"Test templates", "Let's test a template to see the magic."}

	t, err := template.New("todos").Parse("You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
	if err != nil {
		panic(err)
	}
	err = t.Execute(os.Stdout, td)
	if err != nil {
		panic(err)
	}
}
EOF

$ go run main.go
You have a task named "Test templates" with description: "Let's test a template to see the magic."

We can reuse the same template t, without needing to create or parse it again by providing the struct you want to use to the Execute function again:

  ...
	tdNew := Todo{"Go", "Contribute to any Go project"}
	err = t.Execute(os.Stdout, tdNew)
	if err != nil {
		panic(err)
	}
}

As you can see, templates provide a powerful way to customize textual output. Beside manipulating textual output, you can also manipulate HTML output using the html/template package.

Verifying Templates

template packages provide the Must functions, used to verify that a template is valid during parsing. The Must function provides the same result as if we manually checked for the error, like in the previous example.

This approach saves you typing, but if you encounter an error, your application will panic. For advanced error handling, it’s easier to use above solution instead of Must function.

The Must function takes a template and error as arguments. It’s common to provide New function as an argument to it:

t := template.Must(template.New("todos").Parse("You have task named \"{{ .Name}}\" with description: \"{{ .Description}}\""))

Implementing Templates

HTML templating is also common.

$ cat > todo.tpl <<EOF
<!DOCTYPE html>
<html>
  <head>
    <title>Go To-Do list</title>
  </head>
  <body>
    <p>
      To-Do list for user: {{ .User }} 
    </p>
    <table>
      	<tr>
          <td>Task</td>
          <td>Done</td>
    	</tr>
      	{{ with .List }}
			{{ range . }}
      			<tr>
              		<td>{{ .Name }}</td>
              		<td>{{ if .Done }}Yes{{ else }}No{{ end }}</td>
      			</tr>
			{{ end }} 
      	{{ end }}
    </table>
  </body>
</html>
EOF

$ cat > html.go <<EOF
package main

import (
	"html/template"
	"os"
)

type entry struct {
	Name string
	Done bool
}

type ToDo struct {
	User string
	List []entry
}

func main() {
	todos := ToDo{
		User: "Tom",
		List: []entry{
			{
				Name: "TOTO Item 1",
				Done: true,
			},
			{
				Name: "TOTO Item 2",
				Done: false,
			},
		},
	}

	// Files are provided as a slice of strings.
	paths := []string{
		"todo.tpl",
	}

	t, err := template.New("todo.tpl").ParseFiles(paths...)
	if err != nil {
		panic(err)
	}
	err = t.Execute(os.Stdout, todos)
	if err != nil {
		panic(err)
	}
}
EOF

$ go run html.go

Note: In line 67 above, if we use an ad-hoc template name say html-template , we would get error: panic: template: "html-template" is an incomplete or empty template

t, err := template.New("html-template").ParseFiles(paths...)

So the template name MUST be the FIRST FILE NAME if there are many template files.

How if we have multiple template files?

We need to define the template names:

$ cat > todo.tpl <<EOF
{{ define "todo" }}

<!DOCTYPE html>
<html>
  <head>
    <title>Go To-Do list 1</title>
  </head>
  <body>
    <p>
      To-Do list for user: {{ .User }} 
    </p>
    <table>
      	<tr>
          <td>Task</td>
          <td>Done</td>
    	</tr>
      	{{ with .List }}
			{{ range . }}
      			<tr>
              		<td>{{ .Name }}</td>
              		<td>{{ if .Done }}Yes{{ else }}No{{ end }}</td>
      			</tr>
			{{ end }} 
      	{{ end }}
    </table>
  </body>
</html>
{{ end }}

EOF

$ cat > todo2.tpl <<EOF
{{ define "todo2" }}

<!DOCTYPE html>
<html>
  <head>
    <title>Go To-Do list 2</title>
  </head>
  <body>
    <p>
      To-Do list for user: {{ .User }} 
    </p>
    <table>
      	<tr>
          <td>Task</td>
          <td>Done</td>
    	</tr>
      	{{ with .List }}
			{{ range . }}
      			<tr>
              		<td>{{ .Name }}</td>
              		<td>{{ if .Done }}Yes{{ else }}No{{ end }}</td>
      			</tr>
			{{ end }} 
      	{{ end }}
    </table>
  </body>
</html>
{{ end }}
EOF

$ cat > html.go <<EOF
package main

import (
	"html/template"
	"os"
)

type entry struct {
	Name string
	Done bool
}

type ToDo struct {
	User string
	List []entry
}

func main() {
	todos := ToDo{
		User: "Tom",
		List: []entry{
			{
				Name: "TOTO Item 1",
				Done: true,
			},
			{
				Name: "TOTO Item 2",
				Done: false,
			},
		},
	}

	// Files are provided as a slice of strings.
	paths := []string{
		"todo.tpl",
		"todo2.tpl",
	}

	t, err := template.New("todo").ParseFiles(paths...)
	if err != nil {
		panic(err)
	}
	err = t.Execute(os.Stdout, todos)
	if err != nil {
		panic(err)
	}
}

EOF

$ go run html.go

But we get only first file todo.tpl parsed and printed. Why? The solution here is to refer/include those we want in the first template:

$ cat > todo.tpl <<EOF
{{ define "todo" }}

<!DOCTYPE html>
<html>
  <head>
    <title>Go To-Do list 1</title>
  </head>
  <body>
    <p>
      To-Do list for user: {{ .User }} 
    </p>
    <table>
      	<tr>
          <td>Task</td>
          <td>Done</td>
    	</tr>
      	{{ with .List }}
			{{ range . }}
      			<tr>
              		<td>{{ .Name }}</td>
              		<td>{{ if .Done }}Yes{{ else }}No{{ end }}</td>
      			</tr>
			{{ end }} 
      	{{ end }}
    </table>
  </body>
</html>

{{ template "todo2" . }}

{{ end }}
EOF

Please note this line: {{ template "todo2" . }}

References

Last updated