Go template name

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


Solution 1:

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.

Solution 2:

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

Solution 3:

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