Go template name - go

In the html/template (and text/template) packages, template.New has the following signature:
func New(name string) *Template
What exactly is the name used for? I've scanned the docs (and a bit of source), but to no avail. I just instantiate all of my templates with an empty string and it doesn't seem to make a difference. Why should I bother with a name?
Even for naming templates, the two seem equivalent:
template.Must(template.New("").Parse(`{{ define "body" }}Body{{ end }}`))
template.Must(template.New("body").Parse(`Body`))
https://play.golang.org/p/wKzCHdLf2S

The name of the template–unsurprisingly–is to name the template.
What is it good for? As long as you don't want to refer to the template, it doesn't really matter. But if you want to refer to it, then yes, you refer to it by its name.
When would you want to refer to it? When you want to include a template in another e.g. using the {{template}} action, or when you want to execute a specific template using Template.ExecuteTemplate().
So far so good, but there's still a missing key point. This is not unambiguous / trivial: a template.Template value is "the representation of a parsed template". But the wording here is a little "imperfect". A template.Template value may be (and usually is) a collection of multiple, associated templates. template.Template has an unexported field:
tmpl map[string]*Template // Map from name to defined templates.
This tmpl field holds all other associated templates, templates that are visible to the template, and which can be referred to–yes–by their names.
When you parse multiple templates at once, using Template.ParseFiles() or Template.ParseGlob(), then the templates will be named by the file names, and they will be associated automatically (the above mentioned functions return a single template.Template value, which holds all the parsed templates, associated). Doc of Template.ParseFiles() is clear on this:
ParseFiles creates a new Template and parses the template definitions from the named files. The returned template's name will have the base name and parsed contents of the first file. [...]
When parsing multiple files with the same name in different directories, the last one mentioned will be the one that results. For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template named "foo", while "a/foo" is unavailable.
The template name can come from multiple places:
it can come from the file name (as seen above)
it can be specified explicitly (if defined using the {{define "somename"}} or {{block "somename"}} actions),
or it may be defined as an argument passed to template.New() (function) or Template.New() (method).
Let's see some examples:
func main() {
t := template.Must(template.New("one").Parse(t1src))
template.Must(t.New("other").Parse(t2src))
// error checks omitted for brevity
// Executes default, "one":
t.Execute(os.Stdout, nil)
// Executes explicit, "one":
t.ExecuteTemplate(os.Stdout, "one", nil)
// Executes explicit, "other":
t.ExecuteTemplate(os.Stdout, "other", nil)
}
const t1src = `I'm some template.
`
const t2src = `I'm some OTHER template.
`
Output (try it on the Go Playground):
I'm some template.
I'm some template.
I'm some OTHER template.
If you now go ahead, and change the first 2 lines to this:
t := template.Must(template.New("one").Parse(t1src))
t = template.Must(t.New("other").Parse(t2src))
Then what happens here is that we assigned a new template.Template value to t, which was the result of parsing t2src, so that will be the default, but still both templates can be "reached" from it as they are associated. The output changes to this (try it on the Go Playground):
I'm some OTHER template.
I'm some template.
I'm some OTHER template.
Calling template.New() (function) creates a new template, associated to none. When calling Template.New() (method), the returned template will be associated with (all) the template(s) the method is called on.
Now let's see some examples regarding "embedded" templates.
func main() {
t := template.Must(template.New("one").Parse(t1src))
template.Must(t.New("other").Parse(t2src))
template.Must(t.New("third").Parse(t3src))
t.Execute(os.Stdout, nil)
t.ExecuteTemplate(os.Stdout, "one", nil)
t.ExecuteTemplate(os.Stdout, "other", nil)
t.ExecuteTemplate(os.Stdout, "embedded", nil)
t.ExecuteTemplate(os.Stdout, "third", nil)
}
const t1src = `I'm some template. {{block "embedded" .}}I'm embedded in "one".
{{end}}`
const t2src = `I'm some OTHER template.
`
const t3src = `I'm the 3rd, including everything from "one": {{template "one"}}
`
Output (try it on the Go Playground):
I'm some template. I'm embedded in "one".
I'm some template. I'm embedded in "one".
I'm some OTHER template.
I'm embedded in "one".
I'm the 3rd, including everything from "one": I'm some template. I'm embedded in "one".
It should be obvious now what the role of the template name is, and where it comes from.

It is used to render associated templates.
For instance:
tmpl := template.Must(template.New("body").Parse(`
{{ define "body" }}
Body
{{ end }}
`))
tmpl = template.Must(tmpl.New("base").Parse(`
Start of base template
{{ template "body" }}
End of base template
`))
tmpl = template.Must(tmpl.New("baz").Parse(`
Start of baz template
{{ template "body" }}
End of baz template
`))
tmpl.ExecuteTemplate(os.Stdout, "base", nil)
tmpl.ExecuteTemplate(os.Stdout, "baz", nil)
Play Example
Output:
Start of base template
Body
End of base template
Start of baz template
Body
End of baz template
tmpl.ExecuteTemplate(os.Stdout, "base", nil) will render the template using the "base" template
tmpl.ExecuteTemplate(os.Stdout, "baz", nil) will render the template using the "baz" template

If you don't need the name, you can just use the new builtin with
template.Template:
package main
import (
"os"
"text/template"
)
func main() {
t, err := new(template.Template).Parse("hello {{.}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, "world")
}

Related

Go template post processing: is it possible?

In my template, I use a sub-template that generates a piece of output.
The template output must be shifted though (because the output is in YAML format).
Is there any possibility to post-process template output?
{{ template "subtemplate" | indent 10 }}
This indent 10 is fictional, just to explain what I need.
It is possible (as #icza suggested) to save the output
into a variable and then work with it,
but maybe there is a better, more elegant approach?
{{$var := execTempl "subtemplate"}}
{{$var}}
The closest you can get to {{ template "subtemplate" | indent 10 }} is to define a function that parses and executes the subtemplate and outputs the result as string.
var externalTemplates = map[string]*template.Template{
"subtemplate": template.Must(template.New("subtemplate").Parse(sub_template)),
}
// Executes external template, must be registered with FuncMap in the main template.
func xtemplate(name string) (string, error) {
var b bytes.Buffer
if err := externalTemplates[name].ExecuteTemplate(&b, name, nil); err != nil {
return "", err
}
return b.String(), nil
}
t := template.Must(template.New("t").Funcs(template.FuncMap{
"xtemplate": xtemplate, // register func
}).Parse(main_template))
In the main template you can then use the function like this:
{{ xtemplate "subtemplate" | indent 10 }}
https://play.golang.org/p/brolOLFT4xL

Difference between text/template.Templates and html/template.Templates

Recently, I noticed that Templates() of html/template.Template works differently from the one of text/template.Template.
// go1.12
func main() {
t := template.New( "" )
println( len( t.Templates() ) )
}
The result of this code depends on whether you imported text/template or html/template. You'll notice that the text one prints 0 while the other prints 1. Becuase of this, I looked into the GoDoc and the html one's documentation said that Templates() includes itself -- but no further explanation. And I thought there must be some reason why; why does it have to be different from each other?
Templates returned by text/template.New() and html/template.New() are both incomplete templates without a "body", they cannot yet be used to generate any output. You can verify this if you try to execute them:
t := ttemplate.New("t")
h := htemplate.New("h")
fmt.Println(t.Execute(os.Stdout, nil))
fmt.Println(h.Execute(os.Stdout, nil))
Outputs (try it on the Go Playground):
template: t: "t" is an incomplete or empty template
template: "h" is an incomplete or empty template
Returning incomplete templates in the associated templates has no significance, and is an implementation detail. One package chose to include it, the other chose not to.
Note that if you "complete" the template definitions by actually parsing anything, both will include and return the self template in the associated templates, there is no difference in them:
t := ttemplate.Must(ttemplate.New("t").Parse("t"))
h := htemplate.Must(htemplate.New("h").Parse("h"))
fmt.Println(len(t.Templates()), len(h.Templates()))
fmt.Println(t.Execute(os.Stdout, nil))
fmt.Println(h.Execute(os.Stdout, nil))
This will output (try it on the Go Playground):
1 1
t<nil>
h<nil>

How can I override a template block with an empty template?

Using text/html I define a block in my base template, containing default content. In some situations I would like this block to be empty, so I thought I could just re-define its name and make it contain nothing like:
{{ block "something" . }}
<h1>Default content</h1>
{{ end }}
// later in a place that does not want "something" ...
{{ define "something" }}{{ end }}
Somehow Go seems to think that this definition is "Zero" and will still render the default content unless I put any non-whitespace content into the definition.
I found this issue on the Golang repo which describes the very same thing nicely in a Playground example:
package main
import (
"fmt"
"os"
"runtime"
"text/template"
)
func main() {
fmt.Printf("Version: %q\n", runtime.Version())
t, err := template.New("master").Parse(`{{block "area51" .}}Original Content{{end}}`)
if err != nil {
panic(err)
}
t, err = t.New("other_template").Parse(`{{define "area51"}}{{end}}`)
if err != nil {
panic(err)
}
fmt.Printf("Output:\n")
if err := t.ExecuteTemplate(os.Stdout, "master", nil); err != nil {
panic(err)
}
fmt.Printf("\n")
}
Weirdly, the issue mention it is fixed (and landed in 1.8.1 if I understand it correctly), but it does not work for me, neither with 1.8.1+ nor 1.9.
Is this a bug in Golang or is the approach flawed? Do I need to do anything differently in order to re-define the block so that it renders empty?
This is the expected behavior. This is documented at Template.Parse():
Templates can be redefined in successive calls to Parse, before the first use of Execute on t or any associated template. A template definition with a body containing only white space and comments is considered empty and will not replace an existing template's body. This allows using Parse to add new named template definitions without overwriting the main template body.
So you can't "erase" an already defined template (you can't replace its content to be empty).
If you "conditionally" need it, then use an {{if}} action to decide if the template is to be called. Alternatively you may put an {{if}} inside the template, and the template itself may choose not to render anything. In this case you have to make sure to pass the proper argument that controls what the template will render.
P.S. If you're working with HTML templates, you should always use html/template instead of text/template, as the former provides the same interface as package text/template but also provides contextual escaping to generate HTML output safe against code injection.

Syntax for creating struct map key in Go HTML template

I'm writing a simple Go program to display an HTML table of deployed service versions per environment. My program contains the following structs:
type versionKey struct {
Environment string
Service string
}
type templateData struct {
Environments []string
Services []string
Versions map[versionKey]string
}
As you can see, the Versions map uses a versionKey as a key for a string value e.g. "1.0.0".
I'm passing the templateData struct to an HTML template and ranging over its Environments and Services slices to build the HTML table. The problem is that I need to construct a versionKey for any given intersection of environment and service so I can use it to look up the version from the Versions map and output that value in the table cell.
Within the template I have $environment and $service variables available from the ranges, but I can't work out the Go template syntax to create the versionKey struct.
Here's the template code with the markup omitted:
{{$environments := .Environments}}
{{$services := .Services}}
{{$versions := .Versions}}
{{range $service := $services}}
...
{{range $environment := $environments}}
...
{{index $versions ...? }} // How to create versionKey struct map key here?
...
{{end}}
...
{{end}}
Using only template code you can't. You need some kind of support from the executing Go code to do that. By design philosophy, templates should not contain complex logic. You may argue whether this is complex, but the template syntax has no support for this.
Simplest solution would be to add a Version() method to the templateData struct, which would simply return the version for a given environment and service:
func (t *templateData) Version(environment, service string) string {
return t.Versions[versionKey{
Environment: environment,
Service: service,
}]
}
Using this from the template:
{{range $service := $services -}}
{{range $environment := $environments}}
{{$environment}} - {{$service}} version: {{$.Version $environment $service}}
{{end}}
{{end}}
Testing it:
t := template.Must(template.New("").Parse(templ))
td := &templateData{
Environments: []string{"EnvA", "EnvB"},
Services: []string{"ServA", "ServB"},
Versions: map[versionKey]string{
{"EnvA", "ServA"}: "1.0.0",
{"EnvA", "ServB"}: "1.0.1",
{"EnvB", "ServA"}: "1.0.2",
},
}
if err := t.Execute(os.Stdout, td); err != nil {
panic(err)
}
Output (try it on the Go Playground):
EnvA - ServA version: 1.0.0
EnvB - ServA version: 1.0.2
EnvA - ServB version: 1.0.1
EnvB - ServB version:
Alternatives
Instead of the templateData.Version() method you could just as easily register a function which could create and return a value of type versionKey from a given environment and service. See Template.Funcs() for details. This would be more complicated though, but more flexible as this could be reused elsewhere. See an example of this here: Golang templates (and passing funcs to template). A slight variation of this would be to pass a function value as any other template data instead of registering it as a named function, which can be called.
Another alternative would be to "transform" your Versions field into a map of maps, e.g.:
Versions map[string]map[string]string
Which first could be indexed by environment, then by service, which in the template you can achieve by 2 {{index}} actions. You would have to check if the first indexing yields any results though.

How can I render a template in Go without any variables?

I've loaded a template file into memory with the following code:
t := template.New("master")
tpl, err := t.ParseFiles("templates/index.html")
Now I want to draw that template into a string, so my index.html is pretty empty:
{{define "master"}}
Hello World
{{end}}
I'm just starting out, so I don't have any data yet. Is there a way I can convert the Template object into a string without data?
If your template doesn't (yet) use any variables, you can just pass any value as data to render the template. So, to render the template to stdout, you could for example use:
tpl.Execute(os.Stdout, nil)
If you really want to render the template to a string, you can use a bytes.Buffer as an intermediary:
var buf bytes.Buffer
tpl.Execute(&buf, nil)
str := buf.String()
This is impossible in Go, by design - if you don't have data, the Template package is unnecessary overhead.
If you have no data, just read the file using the io package, instead of using templates.

Resources