GO templates

04/09/2020 templates html text sources


GO templates

Packages text/template, html/template are part of GO standard library. GO templates are used in many GO-programmed software — docker, kubernetes, helm. Many 3rd party libraries are integrated with GO templates, for example echo. Knowing GO templates syntax is pretty useful.

This article consists of text/template package documentation and couple of author's solutions. After describing GO templates syntax we'll dive into text/template и html/template sources.

GO templates are active, which means flow-control instructions such as if, else, range cycles are available here.

GO is strictly typed language, but templates work with all data types, many thanks to reflect package developers.

Let's take a brief look to GO templates syntax basics.

Template calls

All template instructions are set between {{ and }} symbols. Any other text is just plain text that is simply printed to output without any changes.

Template context

We have Execute и ExecuteTemplate GO functions to run templates. Both of them have data interface{} parameter.

Execute(wr io.Writer, data interface{}) error
ExecuteTemplate(wr io.Writer, name string, data interface{}) error

data parameter here is a default template data. It can be accessed from templates as .. Following code prints default data:

{{ . }}

Let's call default data current template context. Some template instructions can change template context.

Далее рассмотрим синтаксические компоненты шаблонизатора GO.

Comments

{{/* comment */}}

Condition operator

{{if condition}} T1 {{end}}

If condition is 0, "", nil or empty array/slice, condition is processed as false, 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}}

Range cycles

One can iterate arrays, slices, maps or channels.

In the following code T1 instruction will be executed on each iteration:

{{range pipeline}} T1 {{end}}
{{range pipeline}} T1 {{else}} T2 {{end}}

Key/value variables for each iteration also can be obtained:

{{range $key, $value := pipeline}} 
{{ $key }}: {{ $value }}
{{end}}

With

{{with pipeline}} T1 {{end}}

If pipeline is equivalent to true (like it is decribed in if explanation), T1 is executed, current template context is set to pipeline.

Templates

One can create template by one of following instructions:

{{block "name" pipeline}} T1 {{end}}
{{define "name" pipeline}} T1 {{end}}

To run template:

{{template "name" pipeline}}

Map elements, struct fields, method calls

GO templates can print data.
For example, one can print struct field or map value. Struct fields to be used in templates should be exported (started with capital letter).
Map keys could start with lowercase letter.
All of them can be chained:

.Field1.Field2.key1
.key1.key2

One can use method calls in templates. Template method should return one value or two values and the last value should be an error. There is such method check in the sources.

GO code:

type myType struct{}
func(m *myType) Method() string {
	return "123"
}

Template code:

.Method()

Standard functions

There are two type of function in GO templates — builtin and user defined.

Function call syntax for every function is following:

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 elemtn;
  • slice x 1 2 — slicing slice/array — s[1:2];
  • len x — obtaining len of slice/array/map;
  • print, printf, println — explicit printing data;

Boolean operators also work as functions:

  • eq arg1 arg2arg1 == arg2
  • ne arg1 arg2arg1 != arg2
  • lt arg1 arg2arg1 < arg2
  • le arg1 arg2arg1 <= arg2
  • gt arg1 arg2arg1 > arg2
  • ge arg1 arg2arg1 >= arg2

Values can be chained to a function with | operator. Such values became the last function argument:

{{"output" | printf "%q"}}

User defined functions

One can define any function in GO to use it later in templates.

GO-code of last function which helps to check if the iterated element is the last in list:

tempTemplate := template.New("main").Funcs(
template.FuncMap{
	"last": func(x int, a interface{}) bool {
		return x == reflect.ValueOf(a).Num()-1
	},
})

Using last in template:

{{ $allKeywords := .Data.Keywords }}
{{ range $k,$v := .Data.Keywords}}
	{{ $v }}{{ if ne (last $i $allKeywords) }},{{ end }}
{{ end }}

Internals

GO templates are using 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 logical branch for every iterated type. Same reflect approach is in the eval field inturction sources.

html/template uses text/template. html/template is specifically designed to identify exactly which type html-content is processed by template (html tag's name, attribute, tag's content, css-content, URL). Based on this content identification there different escape solutions.

Related articles