04/09/2020 templates html text sources
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.
All template instructions are set between {{
and }}
symbols.
Any other text is just plain text that is simply printed to output without any changes.
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.
{{/* comment */}}
{{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}}
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 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.
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}}
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()
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 arg2
— arg1 == arg2
ne arg1 arg2
— arg1 != arg2
lt arg1 arg2
— arg1 < arg2
le arg1 arg2
— arg1 <= arg2
gt arg1 arg2
— arg1 > arg2
ge arg1 arg2
— arg1 >= arg2
Values can be chained to a function with |
operator.
Such values became the last function argument:
{{"output" | printf "%q"}}
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 }}
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.