Revel 模板无原生继承,靠 layout.html + {{.yield}} 模拟,子模板须以 {{template "layout" .}} 开头;结构体字段必须首字母大写才可导出;range 中传参需用 dict 或 struct 显式绑定。

Revel 的模板引擎基于 Go 标准 text/template,不支持传统意义上的“模板继承”(如 Django 或 Jinja2 的 {% extends %}),也没有原生嵌套布局语法;所谓“继承”是靠 template 指令 + 布局文件(layout.html)+ {{.yield}} 模拟实现的,而“嵌套”则依赖结构体导出规则与 range 上下文传递——这两者若配置错,模板直接报 nil pointer dereference 或 unexported field 错误。
Revel 中 layout.html 与 yield 的实际写法
Revel 的“继承”本质是:主模板被当作参数传给 layout.html,layout 内用 {{.yield}} 插入内容。这不是语法糖,而是函数式调用,必须严格匹配签名。
-
layout.html必须定义{{define "layout"}},并在其中写{{.yield}}(不能写成{{template "content" .}}或漏掉点号) - 子模板开头必须是
{{template "layout" .}},且不能在{{define "content"}}外直接写 HTML(否则会被 layout 忽略) - 如果子模板用了
{{template "sub" .}}引入局部片段,需确保sub.html中不依赖未传递的字段(比如没传.User却访问.User.Name)
嵌套结构体字段在模板中不可见的典型原因
常见错误是结构体字段首字母小写,比如 type Data { items []item },此时 .items 在模板里完全不可读,报错信息为 items is an unexported field of struct type interface{}。
- 所有嵌套结构体类型名必须大写:如
Item而非item - 结构体字段名必须大写:如
Items []Item而非items []item - 嵌套结构体内部字段也需导出:如
Item中的Name string,不能是name string - 切片本身可为空,但访问前应加
{{if .Items}}判断,否则{{range .Items}}遇到 nil 会 panic
在 range 中安全传递当前项和索引给子模板
Revel 模板中 range 默认覆盖 .,直接 {{template "item.html" .}} 会把当前元素当根对象传进去;若子模板还需知道索引或父级数据,得显式绑定变量。
- 用
{{range $i, $item := .Items}}提取索引和元素,再通过{{template "item.html" (dict "Item" $item "Index" $i "Parent" $)}}传参(注意:dict是 Revel 扩展函数,不是 Go 原生) - 若不用
dict,可改用匿名 struct:{{template "item.html" (struct "Item" $item "Index" $i "Data" $)}},但需确保子模板接收的是对应字段名 - 避免在
range内直接写{{template "item.html" .}}后又在 item.html 里访问.Items—— 此时.已是单个元素,.Items不存在
最易被忽略的一点:Revel 的 dict 和 struct 函数只在 Revel 自带的 template func map 中注册,如果项目升级过 Revel 版本或手动替换了 template engine,这些函数可能失效;此时要么回退到多层 with 嵌套,要么在控制器里预组装好带索引的结构体切片再传入。


















