
mustache 模板无法直接遍历未命名的结构体切片;必须将切片包装为具名字段(如 `items`),再在模板中使用 `{{#items}}...{{/items}}` 语法迭代,否则循环区块会被忽略,仅渲染静态 html。
Mustache 是一个逻辑无关(logic-less)的模板引擎,它严格依赖数据对象的键名(key name) 进行上下文查找。当你直接传入 []DataKey{...}(一个匿名切片),Mustache 在解析 {{#DataKey}} 时,会尝试在顶层数据对象中查找名为 "DataKey" 的字段——但该字段并不存在,因此整个区块被跳过,只渲染表头。
✅ 正确做法:显式封装为命名字段
你需要将切片包裹在一个结构体或 map 中,赋予其一个明确的、与模板中 {{#xxx}} 匹配的字段名。推荐使用匿名结构体(简洁、类型安全):
viewModel := struct {
Items []DataKey `json:"items"` // 字段名需与模板中一致;json tag 可选(mustache 不强制依赖 json tag,但保持一致更稳妥)
}{
Items: data, // 显式赋值,语义清晰
}
err := mustache.RenderFileInLayout(
"templates/datakeys.html.mustache",
"templates/layout.html.mustache",
user,
viewModel,
)
if err != nil {
log.Printf("Template render error: %v", err)
}⚠️ 注意:Items 字段首字母必须大写(导出),否则 Mustache 无法访问;字段名 Items 将映射为 JSON 键 "items"(Go 默认小写转驼峰规则),因此模板中应使用 {{#items}}(全小写)。
? 更新模板:匹配字段名
修改 datakeys.html.mustache,将 {{#DataKey}} 替换为 {{#items}}:
| # | UserID | DataKey | CreatedAt |
|---|---|---|---|
| {{Id}} | {{UserId}} | {{Data}} | {{CreatedAt}} |
? 补充说明与最佳实践
-
时间字段格式化:Mustache 默认调用 time.Time.String()(如 2006-01-02 15:04:05.999999999 -0700 MST),通常不适用于前端展示。建议在 Go 层预格式化:
type DataKeyView struct { Id int64 `json:"id"` UserId string `json:"user_id"` Data string `json:"data"` CreatedAt string `json:"created_at"` // 已格式化为 "2024-05-20" } // 转换切片 items := make([]DataKeyView, len(data)) for i, d := range data { items[i] = DataKeyView{ Id: d.Id, UserId: d.UserId, Data: d.Data, CreatedAt: d.CreatedAt.Format("2006-01-02"), } }然后模板中直接 {{CreatedAt}} 即可输出友好日期。
-
替代方案:使用 map(灵活性高,适合动态场景):
viewModel := map[string]interface{}{ "items": data, "user": user, } 调试技巧:若仍无输出,可在模板开头添加 {{.}} 查看完整上下文(需引擎支持),或打印 json.Marshal(viewModel) 验证结构是否符合预期。
通过显式命名 + 正确字段绑定,即可让 Mustache 准确识别并迭代结构体切片,彻底解决“只有表头”的问题。










