英文:
Compare two variables inside Go template
问题
在我传递给模板的数据中,我有两个变量Type
和Res.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
当我只比较两个普通字符串时,它可以工作,但我想知道是否有一种惯用的方法来做到这一点。我看到你可以在模板中添加一个函数,但我觉得一定有一种更简单的方法。
英文:
In the data I pass to my template I have the two variables Type
and Res.Type
I want to compare to preselect an option for my select field.
To illustrate my problem I have created this simplified version:
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())
}
It should select the "FindAllString"
option but it only generates the error
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
When I just compare two normal strings it works but I want to know if there is an idomatic way to do this. I have seen that you could add a function to the template but I feel that there must be a simpler way for this.
答案1
得分: 14
问题在于{{range}}
操作会改变(设置)点号(.
),即使在你的情况下使用了循环变量($key
和$value
)。在{{range}}
内部,点号被设置为当前元素。
在{{range}}
内部,你写的是:
{{ if eq $key .Res.Type }}
由于循环中的值是字符串值,.Res.Type
是一个错误,因为字符串值(由点号.
表示的当前元素)没有Res
字段或方法。
使用$
符号来引用模板执行时传递的参数,而不是引用循环值:
{{ 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上尝试简化版本。
阅读这个答案以获取更多见解:https://stackoverflow.com/questions/42507958/golang-template-engine-pipelines/42508255#42508255
英文:
The problem is that the {{range}}
action changes (sets) the dot (.
) even if you use loop variables ($key
and $value
) in your case. Inside a {{range}}
the dot is set to the current element.
And inside {{range}}
you write:
{{ if eq $key .Res.Type }}
Since values in your loop are string
values, .Res.Type
is an error, because there is no Res
field or method of a string
value (the current element denoted by the dot .
).
Use the $
sign to not refer to the loop value, but to the param passed to the template execution:
{{ if eq $key $.Res.Type }}
This will work, but won't give you the desired output, as you have a typo:
res := &Result{Type: "findAllString"}
Use capital letter in Result
as your types
map also contains values with capital letter:
res := &Result{Type: "FindAllString"}
With this you get the desired output (try it on the 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>
Also note that you could simply write the loop like this:
{{range $key, $value := .Types}}
<option value="{{$key}}"{{if eq $key $.Res.Type}} selected{{end}}>{{.}}</option>
{{end}}
Also note that for testing purposes you may simply pass os.Stdout
as the writer for template execution, and you'll see the result on your console without having to create and use a buffer, e.g.:
err = t.Execute(os.Stdout, struct {
Types map[string]string
Res *Result
}{types, res})
Try the simplified version on the Go Playground.
Read this answer for more insights: https://stackoverflow.com/questions/42507958/golang-template-engine-pipelines/42508255#42508255
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论