Golang template engine pipelines
I have a Golang template, defined like this
{{- define "test" -}}
{{- printf "%s" .Name | trunc 24 -}}
{{- end -}}
Then I use it in one of my files:
{{ template "test" . }}
What does the dot mean after "test"? Golang template docs say:
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
But I am not sure what pipeline is. Reading documentation gave no results, could anyone explain once again?
Also, why do we have to start values beginning with dot? E.g. {{ - printf "%s" .Name | trunc 24 -}}
. Is it also a kind of pipeline?
Thank you in advance!
Solution 1:
There are 2 template
packages, text/template
and html/template
.
They have the same interface, but the html/template
package is for generating HTML output safe against code injection, and should be used instead of text/template
whenever the output is HTML.
Since they have the same interface but the html/template
provides some extra functionality (contextual escaping of the inserted data), the basics and principles are only documented at text/html
, and the documentation of html/template
mostly focuses on detailing the extra.
That being said, "pipeline" belongs to the basics. It is documented in text/template
, section Pipelines:
Pipelines
A pipeline is a possibly chained sequence of "commands". A command is a simple value (argument) or a function or method call, possibly with multiple arguments:
Argument The result is the value of evaluating the argument. .Method [Argument...] The method can be alone or the last element of a chain but, unlike methods in the middle of a chain, it can take arguments. The result is the value of calling the method with the arguments: dot.Method(Argument1, etc.) functionName [Argument...] The result is the value of calling the function associated with the name: function(Argument1, etc.) Functions and function names are described below.
A pipeline may be "chained" by separating a sequence of commands with pipeline characters '|'. In a chained pipeline, the result of each command is passed as the last argument of the following command. The output of the final command in the pipeline is the value of the pipeline.
"Arguments" and "pipelines" are evaluations of data.
The "dot" .
is basically a cursor, pointing to somewhere in the data structure you pass when executing the template. The starting value of the dot is the value you pass, but this dot is modified by many actions, such as {{range}}
or {{with}}
.
Execution of the template walks the structure and sets the cursor, represented by a period '.' and called "dot", to the value at the current location in the structure as execution proceeds.
So when you write .Name
, that means that the value where dot is pointing currently, you want to refer to its field or method or key called Name
. For example if you pass a struct
, at the beginning of your template .Name
will denote the struct field Name
if it exists, or its method named Name()
.
When you invoke / include another template, you have the possibility to tell what value you what to pass to its execution. When you write {{template "something" .}}
, that means you want to pass the value currently pointed by dot to the template execution. If you want to pass only the Name
field of the struct pointed by the dot, you may do it like {{template "something" .Name}}
.
The value you pass as the pipeline in {{template}}
will become the dot inside the invoked other template.
So as your template is being processed / rendered, the dot may be changed and point "only" to a part of the value originally passed to your template execution. Often it's handy or required to still reach the original value and not just the cursor. For this the template package provides the $
:
When execution begins, $ is set to the data argument passed to Execute, that is, to the starting value of dot.
So even if you're inside a {{range}}
for example (which sets the dot to the successive elements of the array / slice / map you're ranging over), you can still reach out and refer to any other parts of the value passed to the template execution.
So for example if you're ranging over a slice of books like {{range .Books}}
, and if you need the Name
field of the originally passed struct, you may do it inside {{range}}
like this:
{{range .Books}}
Title: {{.Title}}
Original name: {{$.Name}}
{{end}}