The idea is to define a variable for a go template which is also a template using variables (a nested template) like this:
package main
import (
"os"
"text/template"
)
type Todo struct {
Name string
Description string
Subtemplate string
}
func main() {
td := Todo{
Name: "Test name",
Description: "Test description",
Subtemplate: "Subtemplate {{.Name}}",
}
t, err := template.New("todos").Parse("{{.Subtemplate}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
if err != nil {
panic(err)
}
err = t.Execute(os.Stdout, td)
if err != nil {
panic(err)
}
}
The result of the code above is however:
Subtemplate {{.Name}} You have a task named "Test name" with description: "Test description"
means the variable .Name
in the subtemplate is not resolved (probably by design not possible, would require some kind of a recursive call). Is there any/other way to achieve this effect?
It should work for the template functions defined using template.FuncMap
too. Thanx.
CodePudding user response:
You can register a function which parses a string template and executes it.
The function could look like this:
func exec(body string, data any) (string, error) {
t, err := template.New("").Parse(body)
if err != nil {
return "", err
}
buf := &strings.Builder{}
err = t.Execute(buf, data)
return buf.String(), err
}
You pass the template body text and the data for template execution to it. It executes it and returns the result.
Once registered, you can call it like this from a template:
{{exec .Subtemplate .}}
Full example:
td := Todo{
Name: "Test name",
Description: "Test description",
Subtemplate: "Subtemplate {{.Name}}",
}
t, err := template.New("todos").Funcs(template.FuncMap{
"exec": func(body string, data any) (string, error) {
t, err := template.New("").Parse(body)
if err != nil {
return "", err
}
buf := &strings.Builder{}
err = t.Execute(buf, data)
return buf.String(), err
},
}).Parse("{{exec .Subtemplate .}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
if err != nil {
panic(err)
}
err = t.Execute(os.Stdout, td)
if err != nil {
panic(err)
}
This will output (try it on the Go Playground):
Subtemplate Test name You have a task named "Test name" with description: "Test description"
Note that if the subtemplate does not change during runtime, you should pre-parse it and store the resulting template (*template.Template
) to avoid having to parse it each time you execute the template.
CodePudding user response:
You can render twice if you don't mind for peformance ...
package main
import (
"os"
"text/template"
"bytes"
)
type Todo struct {
Name string
Description string
Subtemplate string
}
func main() {
td := Todo{
Name: "Test name",
Description: "Test description",
Subtemplate: "Subtemplate {{.Name}}",
}
t, err := template.New("todos").Parse("{{.Subtemplate}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
if err != nil {
panic(err)
}
buffer := &bytes.Buffer{}
err = t.Execute(buffer, td)
tc, err := template.New ("todo2").Parse(string (buffer.Bytes ()))
if err != nil {
panic (err)
}
err = tc.Execute(os.Stdout, td)
if err != nil {
panic(err)
}
}