In Go's html/template, what do the {{define}} and {{template}} actions do, and when do you need ExecuteTemplate rather than Execute?
answer
- one value, many named templates
- a set with a shared namespace
- the receiver has a name too
- select by name, or run the receiver
basics
~20 s{{define "name"}}...{{end}} names a template inside the parsed text; {{template "name" .}} renders it in place with the data you give it. Execute runs the receiver template; ExecuteTemplate picks one associated template from the set by name.
solid answer
~50 sA parsed `*template.Template` is not one template but a *set*: every `{{define "name"}}...{{end}}` adds another named template to a shared namespace, and `{{template "name" .}}` executes one of them inline, passing whatever pipeline follows the name as the new dot inside it. `Execute(w, data)` runs the template the receiver itself is named for, which is often not what you want: a file made of nothing but `{{define}}` blocks has an empty body of its own, and a template value created with `template.New("root")` has no body at all once files supply the names. `ExecuteTemplate(w, "name", data)` selects from the set by name, so it is the call you make when a layout and its pages were parsed together. `Lookup("name")` returns nil for a name that is not defined, which is the cheap way to check before executing one.
code
go · 6 linest := template.Must(template.New("root").Parse(
`{{define "greeting"}}Hello, {{.Name}}!{{end}}`))
// t.Execute would run "root", whose own body is empty.
err := t.ExecuteTemplate(os.Stdout, "greeting", struct{ Name string }{"Ada"})
// prints: Hello, Ada!go deeper
Be ready to write the two-file shape by hand: one file of {{define}} blocks, a layout that calls {{template}}, one ExecuteTemplate naming what you want, and a check on the returned error.
Explain that a parsed template value is a namespace of associated templates, that the receiver carries its own name, and what dot is inside an invoked template when no pipeline is supplied.
Show how the set stays predictable in a service: parse once at startup under template.Must, render into a buffer so a mid-render error cannot emit half a page, and validate any externally supplied name with Lookup.
Own the convention. Whether pages are addressed by file base name or by an explicit define name is a codebase-wide call that decides how badly a directory reshuffle can break rendering.
## One value, many templates The thing `template.New`, `Parse`, `ParseFiles` and `ParseFS` hand back is a `*template.Template`, and the mental model most people start with — "this variable is my template" — is wrong in a way that produces a very specific class of bug. The value is a *handle into a namespace*. It has its own name (`t.Name()`), and it is associated with every other template that was parsed alongside it. All of them share one map from name to body. `{{define "name"}} ... {{end}}` is what puts an entry in that map. It may only appear at the top level of the template text — you cannot nest a define inside an `{{if}}`, a `{{range}}`, or another define's body. Everything *outside* any define becomes the body of the template the text was parsed into, which is the receiver's own name. ## Calling one from another `{{template "name" pipeline}}` executes the associated template called `name` at that point and writes its output inline. The value of the pipeline after the name becomes dot inside the invoked template. Nothing is inherited: write `{{template "footer"}}` with no pipeline and dot is nil inside `footer`, so any `{{.Year}}` there fails at execution time. Forward the caller's data explicitly with `{{template "footer" .}}`, or narrow it with `{{template "footer" .Site}}`. The name in a `{{template}}` action is a string constant resolved at execution against the set. That is why a template can call one that is defined later, or in a different file, as long as both ended up in the same set. ## Execute versus ExecuteTemplate `Execute(w, data)` is shorthand for "run me" — the template the receiver is named for. `ExecuteTemplate(w, name, data)` runs the associated template of that name. Both return an error, and both may have already written bytes to the writer before failing, so a handler that writes straight to the response can emit a half-rendered page; rendering into a buffer first and copying it out on success is the usual defence. The standard library's own documentation warns about the mismatch: templates created by `ParseFiles` are named by the base names of the files, so if you started from `template.New("root")` and no file is called `root`, the receiver has no body and `Execute` fails. The fix it recommends is exactly `ExecuteTemplate` with a name you know is in the set. Equally, if a page file contains only a define, executing *it* by its file name writes nothing, because its own body is empty. ## template.Must and where parsing belongs `template.Must(t, err)` takes the two results of a parse, panics if the error is non-nil, and returns the template otherwise. Its whole purpose is to let parsing live in a package-level variable or an init step, so a malformed template stops the process at startup instead of surfacing as an error from a request handler that nobody reads. In a long-running service you parse once, at startup, and treat the resulting set as read-only while requests are served — execution is safe from many goroutines, mutation is not. ## Lookup and the defined set `t.Lookup(name)` returns the associated template with that name, or nil. `t.Templates()` returns them all, and `t.DefinedTemplates()` returns a printable string listing the names, which is what the package itself puts into error messages. If a name ever comes from outside the program — a page identifier in a URL, say — you check it against the set with `Lookup` rather than passing it straight to `ExecuteTemplate`. ## The shape to remember A layout file holds the surrounding document and calls `{{template "content" .}}`. Each page file holds `{{define "content"}} ... {{end}}`. You parse the layout together with one page, and you call `ExecuteTemplate(w, "layout.html", data)` — the layout, not the page. Get those two facts right and most template composition problems never appear.
- What is dot inside a template invoked as {{template "footer"}} with nothing after the name?Nil. The pipeline written after the name becomes the new dot for the invoked template, and omitting it does not forward the caller's dot. Any `{{.Field}}` inside then fails at execution. Write `{{template "footer" .}}` to pass the current data through, or `{{template "footer" .Site}}` to hand it a narrower value.
- What does template.Must add, and why is it used at package level?`template.Must(t, err)` panics when err is non-nil and returns t otherwise. It exists so parsing can happen in a package-level variable or an init step: a malformed template then kills the process at startup, where a deploy will notice, instead of returning an error from a handler at 3am. It is only appropriate where a failure genuinely should be fatal.
- Where in the template text may a {{define}} action appear?Only at the top level. You cannot put a define inside an `{{if}}`, a `{{range}}`, or another define's body. Text outside every define becomes the body of the template the text was parsed into; if a file contains nothing but defines and whitespace, that body is empty and executing it by name renders nothing.
A parsed template value is a card catalogue rather than a single book. Execute reads the one book the catalogue itself is filed under; ExecuteTemplate asks for any title on the shelf.
saying these in an interview costs you the question
- Thinks a parsed template value holds exactly one template
- Calls Execute and expects a named block to render
- Believes {{template}} inherits the caller's dot automatically
- Puts a {{define}} inside a range or if body
- Ignores the error returned by Execute or ExecuteTemplate