Golang template engine pipelines

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}}

Leave a Comment

tech