【问题标题】:Golang template variable issetGolang 模板变量 isset
【发布时间】:2017-06-21 11:41:06
【问题描述】:

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

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 }},但这也给了我同样的错误。

有什么想法吗?

【问题讨论】:

  • 尝试在您的isset 函数中记录a。你看到你在传递什么吗?如果不是,则模板级别存在问题。也许尝试同时传递结构和访问它的键以检查是否 isset,并相应地修改您的函数以接受这两个参数。
  • 如果我的结构包含“电子邮件”,它将起作用,并将其记录在 isset 函数中。如果结构不包含“电子邮件”(这是我的情况),它只会从上面输出错误,不会显示来自 issset 函数的日志。
  • PHP 有 isset($variable) 函数,它返回真/假,那么 GO 呢?如何检查模板中的变量以查看它是否是从处理程序发送的?例如,我只想在用户登录时显示菜单。
  • 试试{{ if .Email }},应该对你有用。
  • 看看here

标签: if-statement go struct go-templates


【解决方案1】:

推荐方式

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

问题,并使用地图避免它

如果模板数据的类型是struct(或指向结构的指针)并且没有具有给定名称的字段或方法,则模板引擎会为此返回错误。

如果您要使用地图,您可以轻松摆脱此错误,因为地图可以使用它们不包含的键进行索引,而 index expression 的结果是值类型的 zero value (而不是错误)。

为了演示,请看这个例子:

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',则地图方法不起作用(即它打印“电子邮件未设置。”而不是“电子邮件为:假”)跨度>
【解决方案2】:

如果您不向模板发送包含变量的数据但使用{{ if .Variable }},您将收到错误:

在 <.variable> 处执行“模板名称”:无法评估类型 handler.Data 中的字段变量

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

我的灵感来自:https://stackoverflow.com/a/31527618/1564840。在该示例中,它们为经过身份验证和未经过身份验证的用户显示不同的 HTML。您会看到,在这两种情况下,它们都会发送“Logged”变量。尝试从结构中删除该变量并执行该函数。您将收到我上面提到的错误。

【讨论】:

    【解决方案3】:

    简单的做法:

    {{ if .Email }}
    

    是使用索引:

    {{ if index . "Email" }}
    

    【讨论】:

    • 我不明白这是如何工作的。请解释? AFAICT 索引在不可枚举时会出错
    • index 函数不适用于struct 类型。
    猜你喜欢
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-22
    • 2019-11-17
    • 2017-03-03
    相关资源
    最近更新 更多