推荐的方法
首先,推荐的方法不是依赖于结构体字段是否存在。当然,模板中可能有可选的部分,但决定是否渲染某个部分的条件应该基于在所有情况下都存在的字段。
问题及使用map避免它
如果模板数据的类型是一个struct
(或指向struct
的指针),并且没有给定名称的字段或方法,则模板引擎会返回一个错误。
如果使用map
,就可以轻松摆脱这个错误,因为map
可以用不包含的键进行索引,而索引表达式的结果是值类型的零值(而不是错误)。
为了演示,看看这个例子:
s := `{{if .Email}}Email is: {{.Email}}{{else}}Email is NOT set.{{end}}`
t := template.Must(template.New("").Parse(s))
exec := func(name string, param interface{}) {
fmt.Printf("\n%s:\n ", name)
if err := t.Execute(os.Stdout, param); err != nil {
fmt.Println("Error:", err)
}
}
exec("Filled map", map[string]interface{}{"Email": "as@as"})
exec("Empty map", map[string]interface{}{})
exec("Filled struct", struct {
Email string
}{Email: "as@as.com"})
exec("Empty struct", struct{}{})
输出结果(在Go Playground上试一试):
Filled map:
Email is: as@as
Empty map:
Email is NOT set.
Filled struct:
Email is: as@as.com
Empty struct:
Error: template: :1:5: executing "" at <.Email>: can't evaluate field Email in type struct {}
坚持使用 struct
并提供 "isset"
如果你必须或想要坚持使用 struct
,那么可以实现并提供这个 "isset" 功能,我将称之为 avail()
。
这个实现使用了反射,为了检查通过名称给出的字段是否存在(可用),(包装器)数据也必须传递给它:
func avail(name string, data interface{}) bool {
v := reflect.ValueOf(data)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return false
}
return v.FieldByName(name).IsValid()
}
示例如下:
s := `{{if (avail "Email" .)}}Email is: {{.Email}}{{else}}Email is unavailable.{{end}}`
t := template.Must(template.New("").Funcs(template.FuncMap{
"avail": avail,
}).Parse(s))
exec := func(name string, param interface{}) {
fmt.Printf("\n%s:\n ", name)
if err := t.Execute(os.Stdout, param); err != nil {
fmt.Println("Error:", err)
}
}
exec("Filled struct", struct {
Email string
}{Email: "as@as.com"})
exec("Empty struct", struct{}{})
输出结果(在Go Playground上尝试):
Filled struct:
Email is: as@as.com
Empty struct:
Email is unavailable.
{{ if .Email }}
,这应该可以解决你的问题。 - Lansana Camarabase.customData
是什么? - Lansana Camara