in my code I'm sending a struct ReturnedResult
to the template:
type ReturnedResult struct {
Result bool `json:"result"`
Message string `json:"message"`
}
I want the template to:
Result
or nilResult
I want to check if it is true
or false
So, i wrote the below:
{{with .Result}}
{{if .Result}}
<div id='foo'>
<i class="far fa-thumbs-up"></i>If report not opened automatically, pls download from <a href={{.Message}}>here</a>
</div>
<script>
url = `{{.Message}}`
window.open(url, "_blank");
setTimeout(function () {document.querySelector('#foo').style.display='none'}, 5000);
</script>
{{else}}
<i class="fas fa-exclamation-triangle">{{.Message}}</i>>
<script>
setTimeout(function () {document.querySelector('#foo').style.display='none'}, 5000);
</script>
{{end}}
{{end}}
Sometime I call the template without data as tmpl.Execute(w, nil)
, some times with data as tmpl.Execute(w, webReturn)
The one without data is working fine, but the one with data is not working and no idea what is wrong.
Below is my full code if required.
// go build -ldflags "-H=windowsgui"
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"text/template"
"github.com/zserge/lorca"
)
// ContactDetails ...
type ContactDetails struct {
Email string
Subject string
Message string
Color1 []string
Color2 []string
}
// ReturnedResult ...
type ReturnedResult struct {
Result bool `json:"result"`
Message string `json:"message"`
}
func index(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("forms.html"))
if r.Method != http.MethodPost {
tmpl.Execute(w, nil)
return
}
r.ParseForm()
fmt.Println(r.Form) // print information on server side.
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["color1"][0])
fmt.Println(r.Form["color1"][1])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
details := ContactDetails{
Email: r.FormValue("email"),
Subject: r.FormValue("subject"),
Message: r.FormValue("message"),
Color1: r.Form["color1"],
Color2: r.Form["color2"],
}
fmt.Printf("Post from website! r.PostFrom = %v\n", r.PostForm)
fmt.Printf("Details = %v\n", details)
//r.FormValue("username")
fmt.Println()
// do something with details
sheetID := "AKfycbxfMucXOzX15tfU4errRSAa9IzuTRbHzvUdRxzzeYnNA8Ynz8LJuBuaMA/exec"
url := "https://script.google.com/macros/s/" + sheetID + "/exec"
bytesRepresentation, err := json.Marshal(details)
if err != nil {
log.Fatalln(err)
}
resp, err := http.Post(url, "application/json", bytes.NewBuffer(bytesRepresentation))
if err != nil {
log.Fatalln(err)
}
// read all response body
data, _ := ioutil.ReadAll(resp.Body)
// close response body
resp.Body.Close()
webReturn := ReturnedResult{}
if err := json.Unmarshal([]byte(data), &webReturn); err != nil {
panic(err)
}
fmt.Println(webReturn.Message)
webReturn.Message = strings.ReplaceAll(webReturn.Message, "&export=download", "")
//tmpl.Execute(w, struct{ Success bool }{webReturn.Result})
tmpl.Execute(w, webReturn)
}
func main() {
// Start Host goroutine
go func() {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./public"))))
http.HandleFunc("/", index)
http.ListenAndServe(":8090", nil)
}()
// Start UI
ui, err := lorca.New("http://localhost:8090/index", "", 1200, 800)
if err != nil {
log.Fatal(err)
}
defer ui.Close()
<-ui.Done()
}
Template;
<title>Form Submittal</title>
<style>
body {
font-family: Ubuntu, "times new roman", times, roman, serif;
}
</style>
<link href="http://localhost:8090/static/fontawesome-free-5.15.2-web/css/all.css" rel="stylesheet"> <!--load all styles -->
<!-- link rel="stylesheet" href="http://localhost:8090/styles/MaterialDesign-Webfont-master/css/materialdesignicons.min.css" -->
<script type="module" src="http://localhost:8090/static/node_modules/@mdi/js/mdi.js"></script>
<script type="module">
// import {mdi-home} from "./node_modules/@mdi/js/mdi.js";
// alert(a);
</script>
<script>
//import { mdi-home } from '/MaterialDesign-JS-master/mdi.js';
function deleteRow(row) {
var i = row.parentNode.parentNode.rowIndex - 2; // this -> td -> tr // -2 because the first 2 rows are used for header
var tbl = document.querySelector('tbody');
if(tbl && tbl.rows.length > 1) {
tbl.deleteRow(i);
Array.from(tbl.rows).forEach((row, index) => {
row.cells[0].innerHTML = index + 1;
});
}
}
function insRow(row) {
var i = row.parentNode.parentNode.rowIndex - 2; // this -> td -> tr // -2 because the first 2 rows are used for header
var tbl = document.querySelector('tbody');
var row = document.createElement('tr');
row.innerHTML=`
<th></th>
<td><input size=25 type="text" name="color1" /></td>
<td><input size=25 type="text" name="color2" ></td>
<td><i class="fas fa-edit" onclick="insRow(this)"></i></td>
<td><i class="fas fa-eraser" onclick="deleteRow(this)"></i></td>
`;
var len = tbl.rows.length;
row.cells[0].innerHTML = len + 1;
tbl.insertBefore(row, tbl.children[i+1]);
Array.from(tbl.rows).forEach((row, index) => {
row.cells[0].innerHTML = index + 1;
});
//tbl.appendChild(row);
}
</script>
<h1>Contact</h1>
Bob lives in a <span class="mdi mdi-home"></span>.
<form method="POST">
<label>Email:</label><br />
<input type="text" name="email"><br />
<label>Subject:</label><br />
<input type="text" name="subject"><br />
<label>Message:</label><br />
<textarea name="message"></textarea><br />
<table>
<caption>Monthly savings</caption>
<thead>
<tr>
<th colspan="5">The table header</th>
</tr>
<tr>
<th>SN</td>
<th>PO number</td>
<th>PO invoice value</td>
<th colspan="2">Action</td>
</tr>
</thead>
<tbody >
<tr>
<th>1</th>
<td><input size=25 type="text" name="color1" /></td>
<td><input size=25 type="text" name="color2" /></td>
<td colspan="2"><i class="fas fa-edit" onclick="insRow(this)"></i></td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="5">The table footer</th>
</tr>
</tfoot>
</table>
<input type="submit">
</form>
{{with .Message}}
{{if .Result}}
<div id='foo'>
<i class="far fa-thumbs-up"></i>If report not opened automatically, pls download from <a href={{.Message}}>here</a>
</div>
<script>
url = `{{.Message}}`
window.open(url, "_blank");
setTimeout(function () {document.querySelector('#foo').style.display='none'}, 5000);
</script>
{{else}}
<i class="fas fa-exclamation-triangle">{{.Message}}</i>>
<script>
setTimeout(function () {document.querySelector('#foo').style.display='none'}, 5000);
</script>
{{end}}
{{end}}
template.Execute()
returns an error
, do check that:
if err := tmpl.Execute(w, webReturn); err != nil {
// Handle error:
log.Printf("Template error: %v", err)
return
}
Here's a minimal reproducible example:
type ReturnedResult struct {
Result bool
Message string
}
func main() {
t := template.Must(template.New("").Parse(src))
if err := t.Execute(os.Stdout, nil); err != nil {
panic(err)
}
p := ReturnedResult{Result: false, Message: "a"}
if err := t.Execute(os.Stdout, p); err != nil {
panic(err)
}
p = ReturnedResult{Result: true, Message: "a"}
if err := t.Execute(os.Stdout, p); err != nil {
panic(err)
}
}
const src = `{{with .Message}}
{{if .Result}}
Message with true result
{{else }}
Message with false result
{{end}}
{{end}}
`
Testing it on the Go Playground, output is:
panic: template: :2:7: executing "" at <.Result>: can't evaluate field Result in type string
goroutine 1 [running]:
main.main()
/tmp/sandbox993188219/prog.go:22 +0x226
The problem is that the {{with}}
action sets the dot, so inside the {{with}}
dot will mean the Message
field, which obviously doesn't have .Result
field (it's not a struct).
To just test (and not change the dot), use a simple {{if}}
:
{{if .Message}}
{{if .Result}}
Message with true result
{{else }}
Message with false result
{{end}}
{{end}}
With this template the output will be (try it on the Go Playground):
<empty output for the nil param>
Message with false result
Message with true result