问题描述
在我的代码中,我向模板发送了一个结构体 ReturnedResult
:
type ReturnedResult struct {
Result bool `json:"result"`
Message string `json:"message"`
}
我希望模板:
- 检查是否有
Result
或 nil - 如果有
Result
我想检查它是true
还是false
所以,我写了以下内容:
{{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}}
有时我将没有数据的模板称为 tmpl.Execute(w,nil)
,有时将数据称为 tmpl.Execute(w,webReturn)
没有数据的可以正常工作,但有数据的却无法正常工作,不知道出了什么问题。
如果需要,下面是我的完整代码。
// 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()
}
模板;
<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,5000);
</script>
{{end}}
{{end}}
解决方法
template.Execute()
返回一个 error
,做检查:
if err := tmpl.Execute(w,webReturn); err != nil {
// Handle error:
log.Printf("Template error: %v",err)
return
}
这是一个最小的可重现示例:
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,p); err != nil {
panic(err)
}
}
const src = `{{with .Message}}
{{if .Result}}
Message with true result
{{else }}
Message with false result
{{end}}
{{end}}
`
在 Go Playground 上测试,输出为:
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
问题在于 {{with}}
操作设置了点,因此在 {{with}}
点内将表示 Message
字段,该字段显然没有 .Result
字段(它不是结构体)。
要进行测试(而不是更改点),请使用简单的 {{if}}
:
{{if .Message}}
{{if .Result}}
Message with true result
{{else }}
Message with false result
{{end}}
{{end}}
使用此模板,输出将是(在 Go Playground 上尝试):
<empty output for the nil param>
Message with false result
Message with true result