
在 Go 模板中,. 代表传入的根数据;若直接传入切片(如 []Item),则 . 就是该切片本身,不支持 .resItems 这类字段访问。要渲染多个切片,需将它们封装进 map 或结构体再传入模板。
在 go 模板中,`.` 代表传入的根数据;若直接传入切片(如 `[]item`),则 `.` 就是该切片本身,不支持 `.resitems` 这类字段访问。要渲染多个切片,需将它们封装进 map 或结构体再传入模板。
当你调用 tmpl.ExecuteTemplate(w, "HomeTemplate", resItems) 时,模板接收到的数据就是 []Item 类型的切片——此时 . 直接指向这个切片,而非一个包含 resItems 字段的对象。因此 {{ range .resItems }} 会报错:cannot evaluate .resItems,因为切片类型没有名为 resItems 的字段。
✅ 正确做法:将多个数据集合统一组织后传递。推荐两种方式:
方式一:使用 map[string]interface{}(轻量灵活)
// main.go 中修改 Index 函数
func Index(w http.ResponseWriter, r *http.Request) {
db := database.DbConn()
defer db.Close() // 注意:defer 应在函数开头后立即声明,避免因 panic 跳过关闭
selDB, err := product.ByID()
if err != nil {
http.Error(w, "DB query failed", http.StatusInternalServerError)
return
}
defer selDB.Close()
var resItems []Item
for selDB.Next() {
var id int
var name, typ string
if err := selDB.Scan(&id, &name, &typ); err != nil {
http.Error(w, "Scan error", http.StatusInternalServerError)
return
}
resItems = append(resItems, Item{Id: id, Name: name, Type: typ})
}
// 构建多数据容器
data := map[string]interface{}{
"products": resItems,
"categories": []string{"Electronics", "Books", "Clothing"},
"tags": []string{"new", "featured", "sale"},
}
tmpl.ExecuteTemplate(w, "HomeTemplate", data)
}
对应模板(tmpl/HomeTemplate.html):
<h2>Products</h2>
{{ range .products }}
<div>{{ .Name }} (ID: {{ .Id }}, Type: {{ .Type }})</div>
{{ end }}
<h2>Categories</h2>
{{ range .categories }}
<span>{{ . }}</span>
{{ end }}
<h2>Tags</h2>
{{ range .tags }}
<code>{{ . }}</code>
{{ end }}
方式二:定义专用视图结构体(类型安全、可读性强)
type HomeView struct {
Products []Item
Categories []string
Tags []string
PageTitle string
}
// 在 Index 中:
view := HomeView{
Products: resItems,
Categories: []string{"Electronics", "Books"},
Tags: []string{"hot", "trending"},
PageTitle: "Welcome to Shop",
}
tmpl.ExecuteTemplate(w, "HomeTemplate", view)
模板中即可安全访问:
<title>{{ .PageTitle }}</title>
{{ range .Products }}<p>{{ .Name }}</p>{{ end }}
{{ range .Categories }}
⚠️ 注意事项:
- 避免在模板中做复杂逻辑(如嵌套条件、数据转换),应由 Go 代码预处理;
- 使用
interface{}时务必确保键名拼写准确,否则模板静默失败; - 若切片可能为空,建议在模板中用
{{ if .products }}...{{ else }}No items{{ end }}做空值防护; -
defer db.Close()放在函数开头后更稳妥(原代码中defer在ExecuteTemplate后,若前面 panic 则不会执行)。
通过合理封装数据结构,你不仅能轻松渲染多个切片,还能提升模板可维护性与后端逻辑清晰度。










