Go template and function - go

In my go code I often use if like this
if user && user.Registered { }
equivalent code in go templates would be
{{ if and .User .User.Registered }} {{ end }}
Unfortunately code in the template fails, if .User is nil :/
Is it possible to achieve the same thing in go templates?

The template and function does not do short circuit evaluation like the Go && operator.
The arguments to the and function are evaluated before the function is called. The expression .User.Registered is always evaluated, even if .User is nil.
The fix is to use nested if:
{{if .User}}{{if .User.Registered}} {{end}}{{end}}
You can avoid the nested if or with by using a template function:
func isRegistered(u *user) bool {
return u != nil && u.Registered
}
const tmpl = `{{if isRegistered .User}}registered{{else}}not registered{{end}}`
t := template.Must(template.New("").Funcs(template.FuncMap{"isRegistered": isRegistered}).Parse(tmpl))
playground example

Another option is to use the {{with}} action instead of the and template function.
Quoting from package doc of text/template:
{{with pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is
executed.
Using {{with}} often results in cleaner and shorter code, as inside the {{with}} the dot . is already set to the non-empty "wrapper", the .User in our case; moreover you don't have to worry about how and if the arguments of the and template function are evaluated.
Your template rewritten:
{{with .User -}}
{{if .Registered}}REGISTERED{{end}}
{{- end}}
Testing it without and with a user:
t := template.Must(template.New("").Parse(tmpl))
fmt.Println("No user:")
if err := t.Execute(os.Stdout, nil); err != nil {
panic(err)
}
u := struct{ Registered bool }{true}
fmt.Printf("User: %+v\n", u)
if err := t.Execute(os.Stdout, map[string]interface{}{"User": u}); err != nil {
panic(err)
}
Output (try it on the Go Playground):
No user:
User: {Registered:true}
REGISTERED

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

Remove spaces around passed argument via html/template

When i pass argument to onclick function i got spaces around that argument, why and how remove them?
t, _ := template.New("").Parse(`<div onclick="test({{.}})">{{.}}</div>`)
t.Execute(os.Stdout, 1)
Result:
<div onclick="test( 1 )">1</div>
playground
Edit:
Updated by Dave help, from template we can do something like this:
t, _ := template.New("").Funcs(template.FuncMap{
"test": func(i interface{}) template.JS {
switch i.(type) {
case int:
s := strconv.Itoa(i.(int))
return template.JS(s)
// other types
default:
panic("bad type")
}
},
}).Parse(`<div onclick="test({{test .}})">{{.}}</div>`)
t.Execute(os.Stdout, 1)
playground
It's a result of Golang doing some things to ensure that malicious JS doesn't end up in your template. If you specify that what you are passing in is safe for javascript, it will work fine.
type JS
Use of this type presents a security risk: the encapsulated content should come from a trusted source, as it will be included verbatim in the template output.
https://play.golang.org/p/TUOECg1YDtl
t.Execute(os.Stdout, template.JS("1"))
Result:
<div onclick="test(1)">1</div>

How can I send the result of named-template to a function [duplicate]

This question already has answers here:
Capture or assign golang template output to variable
(1 answer)
get the value of a go template from inside another template [duplicate]
(1 answer)
Closed 4 years ago.
I am trying to indent the result of a named-template. I have tried all of below syntax. Parentheses around "template name ." do not work either.
{{template "my-org.labels" . | indent 8}}
{{indent 8 template "mbfs-postgres.labels" .}}
{{with template "mbfs-postgres.labels" .}}...
There is not built-in support for sending the results of a template to a function.
It is possible to write a template function to do this: The execTemplate function returns a function that executes a named template in t.
func execTemplate(t *template.Template) func(string, interface{}) (string, error) {
return func(name string, v interface{}) (string, error) {
var buf strings.Builder
err := t.ExecuteTemplate(&buf, name, v)
return buf.String(), err
}
}
Use it like this:
t := template.New("")
t = template.Must(t.Funcs(template.FuncMap{"exec": execTemplate(t), "inent": indent}).Parse(`
The template is: {{exec "labels" . | indent 8}}
{{define "labels"}}Hello from Labels!{{end}}`))
t.Execute(os.Stdout, nil)
There are variations on this basic idea that may or may not be more convenient to use. For example, a value can be passed as an argument to the template instead of using a template function.
type execTemplate struct {
t *template.Template
}
func (et execTemplate) Execute(name string, v interface{}) (string, error) {
var buf strings.Builder
err := et.t.ExecuteTemplate(&buf, name, v)
return buf.String(), err
}
t := template.Must(template.New("").Funcs(template.FuncMap{"indent":
indent}).Parse(`The template is: {{.Execute "labels" . | indent 8}}
{{define "labels"}}Hello from Labels!{{end}}`))
fmt.Println(t.Execute(os.Stdout, execTemplate{t}))

get the value of a go template from inside another template [duplicate]

This question already has an answer here:
Capture or assign golang template output to variable
(1 answer)
Closed 5 years ago.
I have two templates T1 and T2. I want to get the output of T1 and do a some extra processing on it inside T2. My question is:
how do I store the output of T1 in a variable inside T2? Is this even possible?
Here's some pseudo-template:
{{define "T1"}}
{{ printf "%s-%s" complex stuff }}
{{end}}
{{define "T2"}}
{{ $some_var := output_from_template "T1"}} <<<<<<<<<<<
{{ etc }}
{{end}}
There is no builtin support for storing the result of a template in a template variable, only for the inclusion of the result.
But you can register custom functions with any complex functionality you want. You may register a GetOutput function which would execute a template identified by its name, and it could return the result as a string, which you can store in a template variable.
Example doing this:
func main() {
t := template.New("")
t = template.Must(t.Funcs(template.FuncMap{
"GetOutput": func(name string) (string, error) {
buf := &bytes.Buffer{}
err := t.ExecuteTemplate(buf, name, nil)
return buf.String(), err
},
}).Parse(src))
if err := t.ExecuteTemplate(os.Stdout, "T2", nil); err != nil {
panic(err)
}
}
const src = `
{{define "T1"}}{{ printf "%s-%s" "complex" "stuff" }}{{end}}
{{define "T2"}}
{{ $t1Out := (GetOutput "T1")}}
{{ printf "%s-%s" "even-more" $t1Out }}
{{end}}`
Output will be (try it on the Go Playground):
even-more-complex-stuff
The "T1" template simply outputs "complex-stuff", and the "T2" template gets the output of "T1", and concatenates the static text "even-more-" and the result of "T1".
The registered GetOutput function gets the name of a template to execute, executes it by directing its output to a local buffer, and returns the content of the buffer (along with the optional error of its execution).
Edit: I've found an exact duplicate: Capture or assign golang template output to variable

Go can't evaluate field when using range to build from template

I have Files slice of File structure in my Go program to keep name and size of files. I created template, see below:
type File struct {
FileName string
FileSize int64
}
var Files []File
const tmpl = `
{{range .Files}}
file {{.}}
{{end}}
`
t := template.Must(template.New("html").Parse(tmplhtml))
err = t.Execute(os.Stdout, Files)
if err != nil { panic(err) }
Of course I got panic saying:
can't evaluate field Files in type []main.File
Not sure how to correctly display file names and sizes using range in template.
The initial value of your pipeline (the dot) is the value you pass to Template.Execute() which in your case is Files which is of type []File.
So during your template execution the dot . is []File. This slice has no field or method named Files which is what .Files would refer to in your template.
What you should do is simply use . which refers to your slice:
const tmpl = `
{{range .}}
file {{.}}
{{end}}
`
And that's all. Testing it:
var Files []File = []File{
File{"data.txt", 123},
File{"prog.txt", 5678},
}
t := template.Must(template.New("html").Parse(tmpl))
err := t.Execute(os.Stdout, Files)
Output (try it on the Go Playground):
file {data.txt 123}
file {prog.txt 5678}

Resources