Golang template engine pipelines
Asked Answered
I

1

10

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!

Infrequent answered 28/2, 2017 at 11:47 Comment(3)
Roughly: The dot '.' is "the current value. This might be a struct or something more complicated. If . is some struct, than .Name selects the name field of that struct. Without a dot you cold not handle primitive types like e.g. an int (what is the name of an int?). A pipeline is how a value flows through your code and functions. Pipeline is explained in the package doc. You may chain stuff together and this might modify what you stuff into the input of the pipeline.Sheriesherif
@Sheriesherif So, by writing {{template "name" . }} I am binding this template to the current context?Infrequent
Well "binding this template to the current context" sound really strange. You execute the template with name "name" with the current value (the dot).Sheriesherif
W
21

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}}
Wampumpeag answered 28/2, 2017 at 12:0 Comment(1)
Very well explained, and it cleared some of my doubts. ThanksDecorous

© 2022 - 2024 — McMap. All rights reserved.