April 9, 2020 Templates Html Text Sources
Packages text/template and html/template are part of the Go standard library. Go templates are used in many Go-programmed software — Docker, Kubernetes, Helm. Many third-party libraries are integrated with Go templates, for example Echo. Knowing Go template syntax is very useful.
This article consists of text/template package documentation and a couple of author’s solutions. After describing Go template syntax, we’ll dive into text/template and html/template sources.
Go templates are active, which means flow-control instructions such as if, else, and range cycles are available.
Go is a strictly typed language, but templates work with all data types, thanks to the reflect package developers.
Let’s take a brief look at Go template syntax basics.
All template instructions are set between {{ and }} symbols. Any other text is plain text that is simply printed to output without any changes.
We have Execute and ExecuteTemplate Go functions to run templates. Both of them have a data interface{} parameter.
Execute(wr io.Writer, data interface{}) error
ExecuteTemplate(wr io.Writer, name string, data interface{}) error
The data parameter here is the default template data. It can be accessed from templates as .. The following code prints the default data:
{{ . }}
Let’s call the default data the current template context. Some template instructions can change the template context.
{{/* comment */}}
{{if condition}} T1 {{end}}
If condition is 0, "", nil, or an empty array/slice, the condition is processed as false, and T1 won’t execute. Otherwise, T1 will execute.
Variations with else, else if:
{{if condition}} T1 {{else}} T0 {{end}}
{{if condition1}} T1 {{else if condition2}} T0 {{end}}
One can iterate arrays, slices, maps, or channels.
In the following code, the T1 instruction will be executed on each iteration:
{{range pipeline}} T1 {{end}}
{{range pipeline}} T1 {{else}} T2 {{end}}
Key/value variables for each iteration can also be obtained:
{{range $key, $value := pipeline}}
{{ $key }}: {{ $value }}
{{end}}
{{with pipeline}} T1 {{end}}
If pipeline is equivalent to true (as described in the if explanation), T1 is executed, and the current template context is set to pipeline.
One can create a template using one of the following instructions:
{{block "name" pipeline}} T1 {{end}}
{{define "name" pipeline}} T1 {{end}}
To run a template:
{{template "name" pipeline}}
Go templates can print data. For example, one can print a struct field or map value. Struct fields to be used in templates should be exported (started with a capital letter). Map keys can start with a lowercase letter.
All of them can be chained:
.Field1.Field2.key1
.key1.key2
One can use method calls in templates. Template methods should return one value or two values, and the last value should be an error. There is such a method check in the sources.
Go code:
type myType struct{}
func(m *myType) Method() string {
return "123"
}
Template code:
.Method()
There are two types of functions in Go templates — built-in and user-defined.
Function call syntax for every function is as follows:
funcname arg1 arg2 arg3
Standard functions list:
call funcLocation arg1 arg2 — function to call a function with arguments;index x 1 2 3 — obtaining a slice/array/map element;slice x 1 2 — slicing slice/array — s[1:2];len x — obtaining length of slice/array/map;print, printf, println — explicit printing of data;Boolean operators also work as functions:
eq arg1 arg2 — arg1 == arg2ne arg1 arg2 — arg1 != arg2lt arg1 arg2 — arg1 < arg2le arg1 arg2 — arg1 <= arg2gt arg1 arg2 — arg1 > arg2ge arg1 arg2 — arg1 >= arg2Values can be chained to a function with the | operator. Such values become the last function argument:
{{"output" | printf "%q"}}
One can define any function in Go to use it later in templates.
Go code of a last function which helps to check if the iterated element is the last in a list:
tempTemplate := template.New("main").Funcs(
template.FuncMap{
"last": func(x int, a interface{}) bool {
return x == reflect.ValueOf(a).Len()-1
},
},
)
Using last in a template:
{{ $allKeywords := .Data.Keywords }}
{{ range $k,$v := .Data.Keywords}}
{{ $v }}{{ if ne (last $i $allKeywords) }},{{ end }}
{{ end }}
Go templates use the reflect package to work with any data type. For example, range sources in text/template:
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
// ...
switch val.Kind() {
case reflect.Array, reflect.Slice:
// ...
case reflect.Map:
// ...
case reflect.Chan:
// ...
}
}
There is a logical branch for every iterated type. The same reflect approach is used in the evalField instruction sources.
html/template uses text/template. html/template is specifically designed to identify exactly which type of HTML content is processed by the template (HTML tag’s name, attribute, tag’s content, CSS content, URL). Based on this content identification, there are different escape solutions.
The Go blog describes how to use slices. Let’s take a look at slice internals.
Read More → Slice Allocation SourcesIn Go, we have goroutines functionality out of the box. We can run code in parallel. However, in our parallel running code we can work with shared variables, and it is not clear how exactly Go handles such situations.
Read More → Map SourcesThe map programming interface in Go is described in the Go blog. We just need to recall that a map is a key-value storage and it should retrieve values by key as fast as possible.
Read More → Map SourcesWhy regular expressions with dot (".") work differently in Go compared to PHP and JavaScript.
Read More → Regular Expressions Sources