比较Go模板中的两个变量

时间:2017-06-24 08:34:52

标签: html loops go go-templates

在我传递给我的模板的数据中,我有两个变量TypeRes.Type我要比较以预选我的选择字段的选项。

为了说明我的问题,我创建了这个简化版本:

package main

import (
    "bufio"
    "bytes"
    "html/template"
    "log"
)

type Result struct{ Type string }

func main() {
    types := map[string]string{
        "FindAllString":      "FindAllString",
        "FindString":         "FindString",
        "FindStringSubmatch": "FindStringSubmatch",
    }
    res := &Result{Type: "findAllString"}

    templateString := `
    <select name="type">
        {{ range $key,$value := .Types }}
            {{ if eq $key .Res.Type }}
                <option value="{{$key}}" selected>{{$value}}</option>
            {{ else }}
                <option value="{{$key}}">{{$value}}</option>
            {{ end }}
        {{ end }}
    </select>`
    t, err := template.New("index").Parse(templateString)
    if err != nil {
        panic(err)
    }
    var b bytes.Buffer
    writer := bufio.NewWriter(&b)
    err = t.Execute(writer, struct {
        Types map[string]string
        Res   *Result
    }{types, res})
    if err != nil {
        panic(err)
    }
    writer.Flush()
    log.Println(b.String())
}

它应该选择"FindAllString"选项,但它只会生成错误

panic: template: index:4:21: executing "index" at <.Res.Type>: can't evaluate field Res in type string

goroutine 1 [running]:
panic(0x53f6e0, 0xc4200144c0)
    /usr/local/go/src/runtime/panic.go:500 +0x1a1
main.main()
    /home/tobias/ngo/src/github.com/gamingcoder/tmp/main.go:41 +0x523
exit status 2

当我只比较两个正常的字符串时,它可以工作,但我想知道是否有一种惯用的方法来做到这一点。我已经看到你可以在模板中添加一个函数,但我觉得必须有一个更简单的方法。

1 个答案:

答案 0 :(得分:6)

问题是即使您在案例中使用循环变量({{range}}.),$key操作也会更改(设置)点($value)。在{{range}}内,点被设置为当前元素。

在你写的{{range}}里面:

{{ if eq $key .Res.Type }}

由于循环中的值为string值,.Res.Type是错误,因为没有Res字段或string值的方法(表示当前元素)点.)。

使用$符号不引用循环值,而是引用传递给模板执行的参数:

{{ if eq $key $.Res.Type }}

这会有效,但不会给你想要的输出,因为你有一个拼写错误:

res := &Result{Type: "findAllString"}

Result中使用大写字母,因为您的types地图还包含大写字母值:

res := &Result{Type: "FindAllString"}

通过这个,您可以获得所需的输出(在Go Playground上尝试):

2009/11/10 23:00:00 
    <select name="type">
                <option value="FindAllString" selected>FindAllString</option>
                <option value="FindString">FindString</option>
                <option value="FindStringSubmatch">FindStringSubmatch</option>
    </select>

另请注意,您可以像这样编写循环:

{{range $key, $value := .Types}}
    <option value="{{$key}}"{{if eq $key $.Res.Type}} selected{{end}}>{{.}}</option>
{{end}}

另请注意,出于测试目的,您可以简单地将os.Stdout作为模板执行的编写者传递,并且您将在控制台上看到结果而无需创建和使用缓冲区,例如:

err = t.Execute(os.Stdout, struct {
    Types map[string]string
    Res   *Result
}{types, res})

尝试Go Playground上的简化版。

阅读此答案以获取更多见解:golang template engine pipelines