Revel模板不支持直接传多个参数给子模板,需用dict函数将键值对封装为map[string]interface{}后单参数传递,这是官方推荐解法。

Revel模板里不能直接传多个参数给子模板
Go标准html/template只允许{{template}}接收一个参数(即.),Revel沿用这一限制。写{{template "item.html" .User .Role}}会报错:unexpected “.” in template clause。这不是Revel的bug,而是底层模板引擎的设计约束。
用dict函数封装多个变量最实用
注册dict函数后,就能把任意键值对打包成map[string]interface{},再作为单个参数传入子模板。这是官方推荐、社区广泛采用的解法。
在app/init.go的init()函数中添加:
revel.TemplateFuncs["dict"] = func(values ...interface{}) map[string]interface{} {
if len(values)%2 != 0 {
panic("dict: odd number of arguments")
}
dict := make(map[string]interface{})
for i := 0; i < len(values); i += 2 {
key, ok := values[i].(string)
if !ok {
panic("dict: first argument to dict must be string")
}
dict[key] = values[i+1]
}
return dict
}
模板中即可安全使用:
{{template "user-card.html" (dict "user" .CurrentUser "isAdmin" .IsAdmin "count" .TotalUsers)}}
子模板内通过{{.user.Name}}、{{.isAdmin}}、{{.count}}访问——所有字段都在.这个map下,无需额外解构。
不加dict时的替代方案及风险
如果暂时无法改代码注册函数,可临时用以下方式,但各有明显缺陷:
- 传整个
.:写{{template "item.html" .}},子模板里直接用{{.CurrentUser}}等。问题在于污染作用域,若子模板和父模板字段名冲突(如都叫.ID),结果不可控。 - 传结构体指针:控制器里构造
struct{ User *User; IsAdmin bool }再RenderArgs["data"] = data。但每次调用都要新建类型,维护成本高,且容易忘记导出字段导致模板读不到。 - 拼接JSON字符串:用
M.ToJSON转成字符串再{{. | safeJS}}注入JS上下文。仅适用于前端数据初始化,不能用于HTML渲染逻辑。
这些都不是真正“传多个变量”,只是绕开限制的权宜之计。
嵌套range中传参必须显式声明变量
在{{range}}块里调用子模板,最容易犯的错是以为.还代表外层上下文。实际上.已变成当前迭代项,原.Title、.Count全不可见。
正确做法是用$前缀捕获外层变量,并在dict中显式传入:
{{range $i, $item := .Items}}
{{template "list-item.html" (dict "item" $item "index" $i "title" $.Title "totalCount" $.Count)}}
{{end}}
注意$.Title中的$表示根上下文,不是.Title——后者在range内已被覆盖。漏掉$会导致.Title为空,且无任何错误提示。


















