会panic;nil切片或map直接{{range .items}}触发“nil pointer evaluating interface {}.items”崩溃,必须前置{{if .items}}判空,且range需用{{$i, $item := .items}}双变量解构。

模板中直接判空会 panic 吗
会。当传入的切片或 map 是 nil,而模板里写 {{range .items}},Revel 会直接 panic:「template: xxx: nil pointer evaluating interface {}.items」。这不是渲染为空,而是服务端崩溃。空切片(len==0)可以安全遍历,但 nil 不行。
必须前置检查:{{if .items}} 或 {{if not (eq .items nil)}}。注意 eq 在 Revel 中支持宽松比较,{{if eq .items nil}} 也成立,但更推荐用 {{if .items}} —— 它对 nil、空切片、空 map、零值字符串都自然返回 false。
range 遍历时怎么同时拿到索引和元素
必须用双变量解构,且变量名前加 $ 前缀:{{range $i, $item := .items}}。第一个是索引(切片)或键(map),第二个是值。不加 $ 会导致子模板里无法引用,比如 {{template "item.html" $i}} 传的是索引,{{template "item.html" $item}} 传的是当前项。
常见错误:
- 写成
{{range .items}}→ 只能访问{{.}},拿不到索引 - 写成
{{range $i, $item := .}}→ 如果.items是字段名,.此时不是切片,会报错「can't iterate over …」 - 漏掉
$,比如{{range i, item := .items}}→i和item是局部变量,但在嵌套{{template}}里不可见
传 map 或结构体时,.title 为什么取不到
因为 Revel 不自动扁平化参数上下文。如果控制器里写 c.Render(map[string]interface{}{"title": "首页", "items": items}),那么模板根对象就是这个 map,{{.title}} 能取到,但 {{.items}} 也能取到;可一旦你写 c.Render(items)(传结构体指针),{{.Title}} 就得靠结构体字段导出(首字母大写)——而 {{.title}}(小写)永远为空,且不报错。
关键点:
- 字段名大小写必须严格匹配,Go 模板区分大小写
- 未导出字段(小写开头)在模板中不可见,也不触发 error
- 传
map时,{{.}}指向整个 map;传结构体时,{{.}}指向结构体实例,需用{{.Field}}
子模板里怎么安全显示“暂无数据”
不能只靠父模板的 {{if .items}} 包裹 {{template}},因为子模板自己也可能需要独立判断上下文是否为空。比如 item.html 接收的是单个 $item,它本身不会为 nil panic,但若你误把它当结构体用(如 {{.Name}}),而实际传入的是字符串,就会 panic。
稳妥做法:
- 子模板第一行加
{{if .}}判断根上下文是否存在 - 若需访问字段,先
{{if .Name}}再输出,避免{{.Name}}直接调用 - 传参时尽量统一类型,比如始终传
map[string]interface{}或始终传结构体指针,别混用
最易被忽略的是:nil 切片和空切片行为不同,前者必须拦截,后者可直接 range;但开发者常把两者等同,结果线上偶发 panic。


















