Golang template variable isset - if-statement

I have created a function to check if a variable is defined:
fm["isset"] = func(a interface{}) bool {
if a == nil || a == "" || a == 0 {
fmt.Println("is not set")
return false
}
fmt.Println("is set")
return false
}
tmpl := template.Must(template.New("").Funcs(fm).ParseFiles("templates/header.html"))
err := tmpl.ExecuteTemplate(w, "header", templateData)
In the template I have:
{{ if isset .Email }}
email is set
{{ end }}
This function works if the variable is contained by the templateData (which is a custom struct that contains a map and a string), but it gives me an error if the variable doesn't exist.
The error is:
executing "header" at <.Email>: can't evaluate field Email in type base.customData
In my case "base.go" is the handler and "customData" is defined by: type customData struct{..}.
I want to be able to reuse templates and to display some sections only if some variables are sent from the handler. Any idea how can I implement a variable isset check on the template side?
I also tried using: {{ if .Email}} do stuff {{ end }} but this also gives me the same error.
Any idea?

The recommended way
First, the recommended way is not to rely on whether a struct field exists. Of course there might be optional parts of the template, but the condition to decide whether to render a part should rely on fields that exist in all cases.
The issue, and avoiding it using a map
If the type of the template data is a struct (or a pointer to a struct) and there is no field or method with the given name, the template engine returns an error for that.
You could easily get rid of this error if you were to use a map, as maps can be indexed with keys they don't contain, and the result of that index expression is the zero value of the value type (and not an error).
To demonstrate, see this example:
s := `{{if .Email}}Email is: {{.Email}}{{else}}Email is NOT set.{{end}}`
t := template.Must(template.New("").Parse(s))
exec := func(name string, param interface{}) {
fmt.Printf("\n%s:\n ", name)
if err := t.Execute(os.Stdout, param); err != nil {
fmt.Println("Error:", err)
}
}
exec("Filled map", map[string]interface{}{"Email": "as#as"})
exec("Empty map", map[string]interface{}{})
exec("Filled struct", struct {
Email string
}{Email: "as#as.com"})
exec("Empty struct", struct{}{})
Output (try it on the Go Playground):
Filled map:
Email is: as#as
Empty map:
Email is NOT set.
Filled struct:
Email is: as#as.com
Empty struct:
Error: template: :1:5: executing "" at <.Email>: can't evaluate field Email in type struct {}
Sticking to struct and providing "isset"
If you must or want to stick to a struct, this "isset" can be implemented and provided, I'll call it avail().
This implementation uses reflection, and in order to check if the field given by its name exists (is available), the (wrapper) data must also be passed to it:
func avail(name string, data interface{}) bool {
v := reflect.ValueOf(data)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return false
}
return v.FieldByName(name).IsValid()
}
Example using it:
s := `{{if (avail "Email" .)}}Email is: {{.Email}}{{else}}Email is unavailable.{{end}}`
t := template.Must(template.New("").Funcs(template.FuncMap{
"avail": avail,
}).Parse(s))
exec := func(name string, param interface{}) {
fmt.Printf("\n%s:\n ", name)
if err := t.Execute(os.Stdout, param); err != nil {
fmt.Println("Error:", err)
}
}
exec("Filled struct", struct {
Email string
}{Email: "as#as.com"})
exec("Empty struct", struct{}{})
Output (try it on the Go Playground):
Filled struct:
Email is: as#as.com
Empty struct:
Email is unavailable.

If you don't send data containing a variable to the template but you use {{ if .Variable }} you will get the error:
executing "templatename" at <.Variable>: can't evaluate field Variable in type handler.Data
My solution was to send the "Email" variable as a boolean (false) in order to pass the {{ if .Email }} function check. But this is a short term solution that I don't like.
I was inspired by: https://stackoverflow.com/a/31527618/1564840. In that example they show different HTML for authenticated and non-authenticated users. You will see that in both cases they send the "Logged" variable. Try removing that variable from the struct and execute the function. You will receive the error that I mentioned above.

The simple way of doing:
{{ if .Email }}
is to use index:
{{ if index . "Email" }}

Related

How to get current cognito user from within go lambda

I'm having a hard time to get the current Cognito user attributes from within my lambda function, that is written in Go. I'm currently doing:
userAttributes = request.RequestContext.Authorizer["claims"]
And if I want to get the email:
userEmail = request.RequestContext.Authorizer["claims"].(map[string]interface{})["email"].(string)
I don't think this is a good way or even an acceptable way - it must have a better way to do it.
You can use 3rd party library to convert map[string]interface{} to a concrete type. Check the mitchellh/mapstructure library, it will help you to implement in a better way.
So, you could improve your code with this code :
import "github.com/mitchellh/mapstructure"
type Claims struct {
Email string
// other fields
ID int
}
func claims(r request.Request) (Claims, error) {
input := r.RequestContext.Authorizer["claims"]
output := Claims{}
err := mapstructure.Decode(input, &output)
if err != nil {
return nil, err
}
return output, nil
}
And somewhere in your handlers, you could get your claims by calling this method
func someWhere(){
userClaims, err := claims(request)
if err != nil {
// handle
}
// you can now use : userClaims.Email, userClaims.ID
}
Don't forget to change func claims request parameter type according to yours (r parameter).

golang templates processing and generics

I have two golang html templates, as follows:
var m map[string]string
m = make(map[string]string)
m["First"] = `<html>
<body>First template type {{.First}}
</html>`
m["Second"] = `<html>
<body>Second template type {{.SecondF1}} {{.SecondF2}}
</html>`
The first html template takes only one argument, named First whereas the second template needs two arguments, named SecondF1 and SecondF2.
Now I have a struct which has two fields, one for receiving a template name and another for receiving the template arguments.
type tmplReceiver struct {
TmplName string
TmplArgs string // Receives JSON string
}
Now, examples of instances for the above structs could be:
var i, j tmplReceiver
i.TmplName = "First"
i.TmplArgs = `{"Field1": "First Template Argument"}`
j.TmplName = "Second"
j.TmplArgs = `{
"SecondF1": "Second template First Argument",
"SecondF2": "Second template Second Argument"
}`
Now I can get the Template string by using the map, for example:
tmplStr := m[i.TmplName] (or)
tmplStr := m[j.TmplName]
tmpl, _ = template.New("email").Parse(tmplStr)
However, how do I get the template to be executed for all the possible template types, with a single tmpl.Execute statement. In other words, if I want to have the following code:
var buff bytes.Buffer
if err := tmpl.Execute(&buff, tmplPtr); err != nil {
log.Fatal(err)
}
log.Println(buff.String())
How do I get the tmplPtr to be valid, irrespective of how many templates I have (First, Second, etc.) and each of these templates can have a variable number of arguments (First has only one arg, whereas Second has two args, etc.)
I do not want to write N different tmpl.Execute statements with an if block for each template name. Is there any other alternative approach to solve this ? Thanks.
Neither json.Unmarshal nor template.Execute cares about the actual type of the data, this will all be handled at runtime. So you can just parse the json to an interface{} and pass that to your templates. Provided that the json data contains the fields that are expected by the template to which you pass the data, this will just work fine.
Playground link
package main
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
)
var templates = map[string]*template.Template{
"A": template.Must(template.New("A").Parse("{{ .A }}")),
"B": template.Must(template.New("B").Parse("{{ .B }} and {{ .C.D }}")),
}
type tmplReceiver struct {
TmplName string
TmplArgs string
}
func main() {
receivers := []tmplReceiver{
tmplReceiver{"A", `{"A": "Value for A"}`},
tmplReceiver{"B", `{"B": "Value for B", "C": { "D": "Value for D" }}`},
}
for _, receiver := range receivers {
var data interface{}
json.Unmarshal([]byte(receiver.TmplArgs), &data)
var buffer bytes.Buffer
templates[receiver.TmplName].Execute(&buffer, data)
fmt.Println(buffer.String())
}
}
Which prints
Value for A
Value for B and Value for D

How to call Go function from pongo template

I need to create JSON data using few keys of map and need to incorporate into html generated. I am using pongo2 library and want to write custom filter to achieve the same.
<script> {{ CategoryMapping|MycustomFilter }} </script>
and coded custom filter like below.
func init() {
pongo2.RegisterFilter("superfilter", GetCategoryJsonData)
}
func GetCategoryJsonData(CatAttributeMapping *map[string]interface{}, param *int) (*string, *error) {
.....
}
But I am getting below error.
src/util/TemplateFilters.go:10: cannot use GetCategoryJsonData (type func(*int, *int) (*string, *error)) as type pongo2.FilterFunction in argument to pongo2.RegisterFilter
I am following below documentation - https://godoc.org/github.com/flosch/pongo2#FilterFunction
I am new to go and unable to understand what wrong I am doing here. Please guide me for the same.
The problem is that your filter function does not accept or return the right types to match what pongo2 is requiring. Let's walk through the docs and see what they want.
First, take a look at the godoc for RegisterFilterFunction. It says
func RegisterFilter(name string, fn FilterFunction)
This is in the pongo2 package so you should read this as RegisterFilter is a function that accepts two arguments and returns no values. The first argument name is of the builtin type string and the second argument fn is of the type pongo2.FilterFunction. But what is a pongo2.FilterFunction? Well clicking on it we see further down in the doc
type FilterFunction func(in *Value, param *Value) (out *Value, err *Error)
In Go you can make your own types based on any other types including functions. So what pongo2 has done is to create a named type called FilterFunction that is any func which accepts two arguments (both of type *pongo2.Value) and returns two values (one of type *pongo2.value and one of type *pongo2.Error).
To bring it all together we would do something like this:
package main
import (
"fmt"
"log"
"strings"
"github.com/flosch/pongo2"
)
func init() {
pongo2.RegisterFilter("scream", Scream)
}
// Scream is a silly example of a filter function that upper cases strings
func Scream(in *pongo2.Value, param *pongo2.Value) (out *pongo2.Value, err *pongo2.Error) {
if !in.IsString() {
return nil, &pongo2.Error{
ErrorMsg: "only strings should be sent to the scream filter",
}
}
s := in.String()
s = strings.ToUpper(s)
return pongo2.AsValue(s), nil
}
func main() {
tpl, err := pongo2.FromString("Hello {{ name|scream }}!")
if err != nil {
log.Fatal(err)
}
// Now you can render the template with the given
// pongo2.Context how often you want to.
out, err := tpl.Execute(pongo2.Context{"name": "stack overflow"})
if err != nil {
log.Fatal(err)
}
fmt.Println(out) // Output: Hello STACK OVERFLOW!
}

Prevent escaping forward slashes in templates

I'm working on converting a pet project of mine from Python to Go just to help me get a bit familiar with the language. An issue I am currently facing is that it's escaping my forward slashes. So it will receive a string like:
/location/to/something
and it then becomes
%2flocation%2fto%2fsomething
Now, it's only doing this when it's in a link (from what I've been reading this escaping is contextual) so this is what the line in the HTML template looks like:
<tr><td>{{.FileName}}</td></tr>
If possible, how can I prevent this in either the template or the code itself?
This is what my templating function looks like (yes, I know it's hackish)
func renderTemplate(w http.ResponseWriter, tmpl string) {
t, err := template.ParseFiles(templates_dir+"base.html", templates_dir+tmpl)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if tmpl == "view.html" {
err = t.Execute(w, FileList)
} else {
err = t.Execute(w, nil)
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
As the value of .FullFilePath, pass a value of type template.URL instead of string, which will tell the html/template package not to escape it.
For example:
func main() {
t := template.Must(template.New("").Parse(templ))
m := map[string]interface{}{
"FileName": "something.txt",
"FileFullPath": template.URL("/location/to/something"),
}
if err := t.Execute(os.Stdout, m); err != nil {
panic(err)
}
}
const templ = `<tr><td>{{.FileName}}</td></tr>`
Output (try it on the Go Playground):
<tr><td>something.txt</td></tr>
Note that even though forward slashes / are allowed in URLs, the reason why the template package still encodes them is because it analyses the URL and sees that the value you want to include is the value of a URL parameter (file=XXX), and so it also encodes the slashes (so that everything you pass in will be part of the value of the file URL parameter).
If you plan to acquire this file path at the server side from URL parameters, then what the template package does is the correct and proper way.
But know that by doing this, you'll lose the safety that prevents code injection into URLs. If you're the one providing the values and you know they are safe, there is no problem. But if the data comes from a user input for example, never do this.
Also note that if you pass the whole URL (and not just a part of it), it will work without using template.URL (try this variant on the Go Playground):
func main() {
t := template.Must(template.New("").Parse(templ))
m := map[string]interface{}{
"FileName": "something.txt",
"FileURL": "/file?file=/location/to/something",
}
if err := t.Execute(os.Stdout, m); err != nil {
panic(err)
}
}
const templ = `<tr><td>{{.FileName}}</td></tr>`
Also note that the recommended way in my opinion would be to include the file path as part of the URL path and not as the value of a parameter, so instead you should create urls like this:
/file/location/to/something
Map your handler (which serves the file content, see this answer as an example) to the /file/ pattern, and when it is matched and your handler is called, cut off the /file/ prefix from the path r.URL.Path, and the rest will be the full file path. If you choose this, you also won't need the template.URL conversion (because the value you include is not a value of a URL parameter anymore):
func main() {
t := template.Must(template.New("").Parse(templ))
m := map[string]interface{}{
"FileName": "something.txt",
"FileFullPath": "/location/to/something",
}
if err := t.Execute(os.Stdout, m); err != nil {
panic(err)
}
}
const templ = `<tr><td>{{.FileName}}</td></tr>`
Try this on the Go Playground.
Also very important: never parse templates in your handler functions! For details see:
It takes too much time when using "template" package to generate a dynamic web page to client in golang
OK, So the solution I've found (and please post if there's a better one) is based on an answer here.
I changed the struct I was using from:
type File struct {
FullFilePath string
FileName string
}
To this:
type File struct {
FullFilePath template.HTML
FileName string
}
And moved the html into the FullFilePath name, and then placed that in template.HTML so each FullFilePath name I was generating was done like so:
file := File{template.HTML("<a href=\"/file?file=" + path + "\"</a>"), f.Name()}
And my template file line was changed to this:
<tr><td>{{.FullFilePath}}{{.FileName}}</td></tr>

Go Templates: Are Nested Ranges Possible?

This one is seemingly simple but it's driving me insane.
How does one go about referencing a struct element higher in the scope within a nested range in golang templates?
Example:
type Foo struct {
Id string
Name string
}
type Bar struct {
Id string
Name string
}
var foos []Foo
var bars []Bar
// logic to populate both foos and bars
In the template:
{{range .foos}}
<div>Foo {{.Name}}</div>
<div>
{{range ..bars}}
<div>Bar {{.Name}} <input type="text" name="ids_{{..Id}}_{{.Id}}" /></div>
{{end}}
</div>
{{end}}
Obviously ..bars and ..Id don't work, but hopefully my intent is clear. I'd like to iterate through all combinations of Foo and Bar and generate a form element with a name build by both the Foo's Id and the Bar's Id.
The problem is that it seems it is impossible to:
Access bars from inside the scope of the foos range scope
Access Foo's Id from inside the bar's range scope
I have a temporary workaround to this by putting a bunch of redundant fields in both structs, but this seems very ugly to me, violates DRY, and in general feels very wrong.
Is there any way with golang templates to do what I'd like to do?
Yes. I feel as if not finding a solution comes from not reading the text/template package closely enough. If you are using html/template, the syntax is the same (and they tell you to read text/template ;)). Here is a complete working solution for what you might want to do.
Go file:
package main
import (
"bytes"
"io/ioutil"
"os"
"strconv"
"text/template"
)
type Foo struct {
Id string
Name string
}
type Bar struct {
Id string
Name string
}
var foos []Foo
var bars []Bar
func main() {
foos = make([]Foo, 10)
bars = make([]Bar, 10)
for i := 0; i < 10; i++ {
foos[i] = Foo{strconv.Itoa(i), strconv.Itoa(i)} // just random strings
bars[i] = Bar{strconv.Itoa(10 * i), strconv.Itoa(10 * i)}
}
tmpl, err := ioutil.ReadFile("so.tmpl")
if err != nil {
panic(err)
}
buffer := bytes.NewBuffer(make([]byte, 0, len(tmpl)))
output := template.Must(template.New("FUBAR").Parse(string(tmpl)))
output.Execute(buffer, struct {
FooSlice []Foo
BarSlice []Bar
}{
FooSlice: foos,
BarSlice: bars,
})
outfile, err := os.Create("output.html")
if err != nil {
panic(err)
}
defer outfile.Close()
outfile.Write(buffer.Bytes())
}
Note: You can probably do something to not load the file into an intermediate buffer (use ParseFiles), I just copied and pasted some code that I had written for one of my projects.
Template file:
{{ $foos := .FooSlice }}
{{ $bars := .BarSlice }}
{{range $foo := $foos }}
<div>Foo {{$foo.Name}}</div>
<div>
{{range $bar := $bars}}
<div>Bar {{$bar.Name}} <input type="text" name="ids_{{$foo.Id}}_{{$bar.Id}}" /></div>
{{end}}
</div>
{{end}}
The two morals of this story are
a) use variables in templates judiciously, they are beneficial
b) range in templates also can set variables, you do not need to rely solely on $ or .