Golang模板变量是否存在

15

我创建了一个函数来检查变量是否已定义:

fm["isset"] = func(a interface{}) bool {
        if a == nil || a == "" || a == 0 {
            fmt.Println("is not set")
            return false
        }
        fmt.Println("is set")
        return false
    }

tmpl :=  template.Must(template.New("").Funcs(fm).ParseFiles("templates/header.html"))

err := tmpl.ExecuteTemplate(w, "header", templateData)

在模板中我有:

{{ if isset .Email }}
    email is set
{{ end }}

如果变量包含在 templateData 中(这是一个包含映射和字符串的自定义结构体),则此函数有效,但如果该变量不存在,则会出现错误。

错误信息如下:

executing "header" at <.Email>: can't evaluate field Email in type base.customData

在我的情况下,“base.go”是处理程序,“customData”由以下方式定义:type customData struct{..}
我希望能够重用模板,并且仅在处理程序发送某些变量时显示某些部分。您有什么想法如何在模板侧实现变量isset检查?
我也尝试使用:{{ if .Email}} do stuff {{ end }},但这也给我带来了相同的错误。
有什么想法吗?

PHP有isset($variable)函数,它返回true/false,那么GO呢?我该如何检查模板中的变量以查看它是否从处理程序发送?例如,我想仅在用户登录时显示菜单。 - Pascut
尝试简单地使用{{ if .Email }},这应该可以解决你的问题。 - Lansana Camara
base.customData 是什么? - Lansana Camara
基础是处理程序,“base.go”,customData是一个包含映射(“Streets”)和字符串(“Name”)的结构体。如果我向“customData”的结构中添加一个名为“Email”的新字符串,则{{if isset .Email}}函数和{{if .Email}}都可以工作。因此,当我没有发送模板上检查的数据时,问题就会发生。 - Pascut
1
请点击这里查看。 - Lansana Camara
显示剩余4条评论
3个回答

24

推荐的方法

首先,推荐的方法不是依赖于结构体字段是否存在。当然,模板中可能有可选的部分,但决定是否渲染某个部分的条件应该基于在所有情况下都存在的字段。

问题及使用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.

如果映射中的条目值为“false”(即它打印“电子邮件未设置”而不是“电子邮件为:false”),则映射方法无效。 - allsap

0

如果您未将包含变量的数据发送到模板,但使用了{{ if .Variable }},则会出现以下错误:

在执行“templatename”时出错,在<.Variable>:无法在类型handler.Data中评估字段变量

我的解决方案是将“Email”变量作为布尔值(false)发送,以便通过{{ if .Email }}函数检查。但这是我不喜欢的短期解决方案。

我受到了https://dev59.com/Co3da4cB1Zd3GeqPwja2#31527618的启发。在那个示例中,他们为已认证和非认证用户显示不同的HTML。您将看到在两种情况下,他们都发送“Logged”变量。尝试从结构中删除该变量并执行该函数。您将收到我上面提到的错误。


-3

做法很简单:

{{ if .Email }}

使用索引:

{{ if index . "Email" }}

1
我不明白这是如何工作的。能否请您解释一下?据我所知,如果遇到非枚举对象,索引会出错。 - JackyJohnson
“index” 函数无法与类型为 “struct” 的变量一起使用。 - hyunchel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接