Go Templating
Go Templating
com/chap-32-templates
• nesting / nested
• binary
To better understand this definition, we will take the example of an e-commerce website. As a developer, you are instructed to create
product pages. The website has only three products to show. That’s pretty easy; you will create three pages (with HTML and CSS) to present
the products. You will ask the marketing team to produce the commercial texts and give you pictures of the products. This task will not take
you long days to perform.
Imagine now that the marketing guy comes to you ten months later and announces that they will introduce 100 new products to the
catalog. You are asked to develop the pages for the products.
The first solution will take a lot of time for you and the whole team, whereas the second solution seems smarter.
The idea is to develop just one product page. For the product name, you will put a placeholder, for the description another one, and for the
price another one. Then this idea is to inject product data into the right placeholders dynamically.
1 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
A template represents the model of some kind of page (for instance, the product page) or part of a web page (the navigation bar). Data are
injected into a template into placeholders. The underlying data often comes from the application’s persistence layer (a database, a cache
system...). This data is structured to be used easily in the template.
You can also use templates to generate emails, pdfs, or other documents. In this chapter, we will focus on the web use case.
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
4 Why is it useful
• It allows you to gain considerable time when you develop a website. You do not have to repeat yourself.
• The designers are often taught in school to create and maintain templates. Many templating engines exist (for each language) with
their specialties, but they share common features that are easy to understand.
• Designers and programmers can work independently after they have specified the data structure passed to the template.
1. text/template
2. html/template
The first one can be used for text output when there is no injection risk. You can use the second one for formatting HTML pages. In the
second one, Go provides a protection mechanism against bad users that will inject code into their input to attack your website.
If you plan to generate HTML, you should use the html/template package an NOT the text/template !
Note also that you should always validate data injected into templates. You should never trust data generated by users.
The first thing to do is to create the template. Firstly, we will look at a standard HTML page for a product page.
6.1 View
<!--views/product.html-->
<!DOCTYPE html>
<html>
<head>
<title>Red Tea Pot 250ml</title>
</head>
<body>
<h1>Red Tea Pot 250ml</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. </p>
<p>Price : <strong>23.99</strong> $</p>
<h2>Delivery options</h2>
<ul>
<li>Express + 2$ </li>
<li>Ultra Express + 5$</li>
<li>Normal</li>
</ul>
<h3>This was a {{.}}</h3>
</body>
</html>
This page contains details about our product. We have the product name, description, price, and delivery options. We will store this file in
the directory “views”. This name is common in the web industry (it makes an implicit reference to the MVC model: Model View Controller).
2 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
The notation you see (the double curly braces) refers to a template variable. We will see in the next sect how go handles it.
// template/basic/main.go
func main() {
http.HandleFunc("/red-tea-pot", redTeaPotHandler)
if err := http.ListenAndServe("localhost:8080", nil); err != nil {
panic(err)
}
}
// template/basic/main.go
func redTeaPotHandler(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("./views/product.html")
if err != nil{
http.Error(w, "Something went wrong", http.StatusInternalServerError)
return
}
We have defined a request handler for our server in the last code snippet. It takes as argument a http.ResponseWriter and a pointer to a
http.Request .
It’s a classical HTTP handler. If you want to get more information about how to create a web server take a look at the dedicated chapter
([chap:Basic-HTTP-Server]).
For each of the file paths in arguments, the function template.ParseFiles will :
• Generate the name of the template based on the last element of its path. In our case, the name will be “product.html”
Then once we have loaded and parsed our template, we will call the method Execute . It takes to arguments an io.Writer and a second
one which represents the data to inject into the template.
We have nothing to inject in our example because our template is not dynamic; every element is fixed.
7 Template actions
In a template, you can add “actions” that will indicate to the system to do something. The official definition of actions are “data evaluations
or control structures”.
3 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
Here we are asking the templating engine to print the value of the second argument of the template.Execute method. {{ . }} is a
template directive. The template engine defines a pseudo-scripting language. You will see that this scripting language is very close to Go.
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
8 Dot notation
The dot sign represents the data passed to the template. To access a property, you just have to write a dot then the property’s name. For
instance, if I want to access to the property Price from the data passed to the template, I use the following syntax :
{{.Price}}
In this configuration with the dot notation, we are using the global context. This is not always true. When you use the dot inside an
iteration, the dot do NOT represent the global context, but the iteration context. Let’s take an example you will understand immediately.
If you have defined the property Price, you can access it from everywhere in the template like this :
We have two notations .Price and $.Price point to the same value. The dot gives you access to the global data context of the
template.
{{range .ShippingOptions}}
<li>{{ . }}</li>
{{end}}
Here the dot is equal for the first iteration to "Extra Priority" then to "Normal" and finally to "Low Priority" . The template engine
will output :
<li>Extra Priority</li>
<li>Normal</li>
<li>Low Priority</li>
But what if you want to access the property Price (which is in the global context)? With the $ sign you can access the global context
{{range .ShippingOptions}}
<li>Product Price : {{ $.Price}} : {{ . }}</li>
{{end}}
9 Print text
This is the main usage of a template. The backend gives you a dataset, and you have to inject it into an HTML file.
The first thing to do is to define a type struct that will structure the data :
We have a very simple type struct with three text fields. Then we create a variable of this type :
teaPot := Product{Name: "Red Tea Pot 250ml", Description: "Test", Price: "19.99"}
4 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
The variable teaPot contains all the data necessary to fill a product page :
<!DOCTYPE html>
<html>
<head>
<title>{{.Name}}</title>
</head>
<body>
<h1>{{.Name}}</h1>
<p>{{.Description}}</p>
<p>Price : <strong>{{.Price}}</strong> $</p>
<h2>Delivery options</h2>
<ul>
<li>Express + 2$ </li>
<li>Ultra Express + 5$</li>
<li>Normal</li>
</ul>
</body>
</html>
Note that each action begins with a point "." . We will inject the property Name into the HTML tag title. Name is also injected into the
tag h1.
To inject the value of the property Foo we just have to use the following action :
{{.Foo}}
Once our view and the data variable are ready, we just have to pass them to the Execute method :
{{- .MyVar}}
5 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
To remove white spaces in what follows your directive, use this syntax :
{{.MyVar -}}
Note that it will not trim the variable MyVar but the text surrounding the action.
10 Print dates
Dates are very common on web pages. You can pass a time.Time value to a template. Let’s add the field ShippingDate to our type struct
:
teaPot := Product{Name: "Red Tea Pot 250ml", Description: "Test", Price: "19.99", ShippingDate: time.Now()}
But when the template got executed, the following string appears :
This is not a very user-friendly way of format a date! With templates you can call the Format method :
Here "2006-01-02" is the date layout. You can adapt it for your needs.
11 Nested templates
If your site has more than 1 page, you might consider using nested templates. Why? To avoid repeating yourself when coding templates.
For instance, on each page of your website, you will have (practically) the same header, the same footer, the same navbar.
If you choose to copy-paste the code of the navbar on each page of the site you miss one of the most interesting features of a templating
engine : nested templates (also called “partials” on some publications).
6 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
// views/header.html
{{ define "header" }}
<!DOCTYPE html>
<html>
<head>
<title>{{ .PageTitle }}</title>
</head>
<body>
{{ end }}
The footer template is closing the body tag and closing the HTML tag :
{{ define "footer" }}
</body>
</html>
{{end}}
Note that we used a new action : define . With it, you can define the contents of a named template :
{{ define "X" }}
<!--Content of the template named X -->
{{ end }}
Nested templates[fig:Nested-templates]
// views/product.html
{{ template "header" . }}
<h1>{{.Name}}</h1>
<p>{{.Description}}</p>
<p>Price : <strong>{{.Price}}</strong> $</p>
<p>Shipping Date : {{.ShippingDate.Format "2006-01-02"}}</p>
<h2>Delivery options</h2>
<ul>
<li>Express + 2$ </li>
<li>Ultra Express + 5$</li>
<li>Normal</li>
</ul>
{{ template "footer" . }}
To call a specific template (by its name), we are using the following action syntax :
{{ template "header" . }}
Where we are calling the template named "header" and we are giving it the dot context. This way the “header” template can have access
7 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
to all variables that are defined in this context. The product page is defined afterward. At the end of the page, we are calling the “footer”
template with a similar syntax.
11.0.0.3 Backend
In the backend, do not forget to load and parse the three templates to make this works :
// template/nested/main.go
// ...
Note that here we are loading the product template first. If you load one of the two others first, you will end up will a blank page if you
use the following syntax into your handler :
If you want to specify which template to execute (to avoid the blank page error), use this syntax instead :
Here we are telling go to execute the template named "product.html" (the default name of a template is the last part of its path).
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
12 Variables
Sometimes it’s useful to store something in a variable for future use. Variable names start with the dollar sign "$" . You can use the short
assignment statement to create a variable :
{{ $myVar := .Name }}
In the previous code snippet, we created the variable $myVar and assigned to it the value contained in .Name . The left part of the
assignment can be more complex. In the official documentation, we name the left part of the assignment a “pipeline”.
You can change the value of an existing variable using the following syntax :
{{ $myVar = .Price }}
• If you define a variable inside a complex structure (for instance, an if statement) then the variable exists only inside the scope of that
structure :
{{ if .Price}}
{{ $var2 := .Name}}
// $var2 exists
{{ end}}
// $var2 no longer exists
<p>{{ $var2 }}</p>
Here we define the variable $var2 “inside” an if statement. The variable $var2 only exists in the scope of the if statement. After the
{{end}} the variable cannot be used.
13 Call a method
We can call methods into templates. Not all methods can be called :
8 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
◦ If two values are returned, the second one must be of type error .
Let’s take an example. We will define the methods Foo and Bar with a Product receiver :
// template/method-call/main.go
//...
<p>{{.Foo}}</p>
<p>{{.Bar "lol"}}</p>
<p>FOO</p>
<p>Bar : lol</p>
This is a variadic function. It will check the “boolean” value of each argument. When it encounters a false, it will return it directly. When all
arguments are true, the function will return the last argument.
14.0.0.2 Example:
Let’s add the property Sale (bool) and SaleImagePath (slice of strings) to the data struct type ( Product ). The idea is to add an image to
the page if the product is on sale and if we have at least an image to display.
We will initialize the value of the property Sale to true and the property SaleImagePath with a slice of strings containing one string. The
function call :
• .Sale is true
9 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
The first argument of this function is... a function/method name. Call allows you to use a function directly inside the template. There are
some limitations to the function. It must return one or two values (if two, the second argument must be an error). The function can have as
many arguments as you want.
// template/functions/main.go
This function concatenates to string (with a bytes.Buffer ). If we want to call it inside our template, we must modify our data struct and
add the function to it.
// template/functions/main.go
We name this property MyFunc . Then we create a new instance of Product , and we assign to MyFunc the Bar function :
teaPot := Product{
//...
MyFunc:Bar}
Which outputs :
<p>firstsecond</p>
Can be used to print something; the functions called behind are the ones from the fmt package ( fmt.Sprint , fmt.Sprintf ,
fmt.Sprintln ).
This function takes a variable number of arguments. It will build a string from those arguments and then make a call to a function of the
package url : QueryEscape . This function allows you to build complex query parts of URLs directly into your template
• https://golang.org/pkg/text/template/#hdr-Functions
We will create a template.FuncMap . The type behind FuncMap is map[string]interface{} . It’s a map of strings to empty interface. Then
we can add to our map the functions we want to add to the template:
10 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
Here we create an anonymous function that will wrap the function string.Title (which capitalize the first letter of every word of a
sentence). The function is stored into a variable named capitalizeFirstLetter .
The key of this map is the name that will be exposed into the template, the second argument is the function itself. We can not directly call
ParseFiles , because the functions have to be added to the template before parsing :
// template/custom-functions/main.go
//...
<p>{{capitalizeFirstLetter "test"}}</p>
Will output :
Test
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
16 Conditionnals
You can use conditional structures (if, then else).
Let’s take an example: In our previous product example, we will add a field to the type struct Product :
The field is named Sale . It’s a boolean that will flag a product if it’s on sale. We want to display something special on our page for this
occasion, but only if the product is on sale :
{{ if .Sale }}
<h3>Exclusive Sale Today !</h3>
{{ end }}
When the template is executed, the engine will check if the value of Sale is not empty. The “empty” condition covers the following case :
• false
•0
• a nil pointer
• an interface value
11 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
{{ if .Sale }}
<h3>Exclusive Sale Today !</h3>
{{ else }}
<p>Not in sale, sorry</p>
{{ end }}
For more complex comparisons, you can use the eleseif action :
{{ if .Sale }}
//...
{{ elseif .ExtraDiscount }}
//...
{{ else }}
//...
{{ end }}
17 Comparison operators
You can compare two variables together by using the six existing operators. The syntax is not usual for comparisons. You first specify the
comparison operators, and then you specify the two operands. For instance, if you want to compare .Price and 100.00 together the
syntax is eq .Price 100.00 :
{{ if eq .Price 100.00 }}
<p>Only 100$</p>
{{ else }}
<p>Give yourself a treat</p>
{{ end }}
Those comparisons will return boolean values that can be used safely with conditionals statements.
12 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
17.0.0.1 Warning !
• It’s better to compare variables that have only the same type. The package allows comparing variables that have not the same type. I
do not recommend that because it can lead to errors :
Let’s take a concrete example : .Price is of type float64 (defined in our data struct), 100 is parsed as an integer. The two types are
not the same. The result of the comparison is false (event if .Price has the value one hundred). The text <p>Only 100$ it's a
deal!</p> * will not appear on the HTML page when the template will execute.
{{ if eq .Price 100 }}
<p>Only 100$ it's a deal!</p>
{{ end }}
◦ bool
◦ uintptr
◦ float32, float64
◦ complex64, complex128
◦ string
You can define a variable of type LogLevel and use it in your template to make comparisons
18 Iteration
Comments, reviews, delivery options... etc. Data to display in a template often comes in the form of lists. The Go template engine has
actions dedicated to iteration.
For instance, let’s add the property ShippingOptions to our Product type struct (the data that we will pass to the template) :
// template/iteration/main.go
//...
ShippingOptions is a slice of strings. Then in our template, we can iterate through this slice with the range action :
// template
<ul>
{{range $index, $element := .ShippingOptions}}
<li>{{ $element }}</li>
{{end}}
</ul>
The range action will launch an iteration over the elements of ShippingOptions . After range, you have to provide two variables names;
the first is for the index ( $index ), the second is for the element under that index ( $element ).
We can then use those two variables inside the range loop (which is delimited by a end action). The variables $index and $element
13 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
<ul>
<li>Extra Priority</li>
<li>Normal</li>
<li>Low Priority</li>
</ul>
You can also use a shorter version to iterate over a slice in a template :
{{range .ShippingOptions}}
<li>{{ . }}</li>
{{end}}
//template
{{ $firstDelOpt := index .ShippingOptions 0}}
<p>First delivery option is {{ $firstDelOpt }}</p>
The value of .ShippingOptions[0] . is assigned to the variable $firstDelOpt . We can then use this variable in the rest of the template.
We can use the index function with multi-dimensional slices. Let’s add a property Notes (a two-dimensional slice) to our type struct
Product :
This 2D slice represents the notes of users for a product. Each row represent a notation, each column represents the note for a criterion :
notes := [][]int{}
note1 := []int{1, 2, 3}
note2 := []int{4, 5, 6}
notes = append(notes, note1)
notes = append(notes, note2)
Here we have two notes ( note1 and note2 ) with three criteria each. To retrieve the second criterion of the first note inside our template
we can use the index function like this :
Here we provide three arguments to index; the first one is our 2D slice the two others are the indices. Here the value of .Notes[0][1] is
retrieved (the first element of a slice is at index 0, then second at index 1).
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
Let’s say that your template files are located in a views dir. You can embed all files located into the views directory with the //go:embed
views/* directive.
14 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
When the program is compiled, Go will populate views with all files located into the views directory.
// template/basic-embed/main.go
package main
import (
"embed"
"html/template"
"net/http"
"time"
)
//go:embed views/*
var views embed.FS
func main {
tmpl, err := template.ParseFS(views, "views/product.html")
if err != nil {
http.Error(w, "Something went wrong", http.StatusInternalServerError)
return
}
// use the template
}
Then you can use the function template.ParseFS to load and parse your template files.
21 Debugging a template
During the development phase, you can use this technique to improve your productivity :
{{ printf "%#v" . }}
This will output (in a human-readable mode) the properties keys and values passed to the template engine. Here is an example :
22 Test yourself
22.1 Questions
1. When you iterate over a slice with range how to print the slice elements values?
2. True or False. To generate web pages templates, you can use the two following packages: text/template and html/template .
22.2 Answers
1. When you iterate over a slice with a range, how to print the slice elements values?
1. With {{ . }}
2. True or False. To generate web pages template, you can use the two following packages: text/template and html/template .
1. It is technically true.
15 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
3. The package html/template offers an extra layer of security (sanitization of data injected into the templates)
1. Given that you have a field ShippingDate of type time.Time you can do the following :
2. {{.ShippingDate.Format "2006-01-02"}}
1. {{ template "header" . }}
7. How to embed templates file in your Go binary?
23 Key Takeaways
• Go has a built-in template engine.
• A template engine will combine a template with data to produce a final document.
• This package offers a security layer to protect your generated documents against common attacks.
• You should always validate data generated by the user against restrictive rules.
template/text is designed to produce text documents. It does not offer the same security features as template/html
• You can call methods in templates : {{ .MyFunc "arg1""arg2" }} . Arguments follow the name of the method
• Then you can “inject” this template into another one with : {{ template "header" . }}
• You can declare variables with the following syntax : {{ $var2 := .Name}}
• To iterate over a slice and array or a map, you can use range
<ul>
{{range $index, $element := .ShippingOptions}}
<li>{{ $element }}</li>
{{end}}
</ul>
• You can use predeclared global functions : and , or , not , .… a complete list can be found here : https://golang.org/pkg/text
/template/#hdr-Functions
• From Go 1.16, you can easily embed your templates into your binary file with //go:embed
16 of 17 02/01/2023, 02:19
Templates - Practical Go Lessons https://www.practical-go-lessons.com/chap-32-templates
//go:embed views/*
var views embed.FS
//...
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
Bibliography
Previous Next
Table of contents
Did you spot an error ? Want to give me feedback ? Here is the feedback page! ×
Newsletter:
Like what you read ? Subscribe to the newsletter.
Practical Go Lessons
By Maximilien Andile
Copyright (c) 2023
Follow me Contents
Posts
Book
Support the author Video Tutorial
About
The author
Legal Notice
Feedback
Buy paper or digital copy
Terms and Conditions
17 of 17 02/01/2023, 02:19