我正在编写一个简单的Go程序,以显示每个环境的已部署服务版本的HTML表。 我的程序包含以下结构:
type versionKey struct {
Environment string
Service string
}
type templateData struct {
Environments []string
Services []string
Versions map[versionKey]string
}
如您所见,Versions映射使用versionKey作为字符串值的键,例如 "1.0.0"。
我将templateData结构传递给HTML模板,并遍历其Environments和Services切片以构建HTML表。 问题是我需要为任何给定的环境和服务的交集构造一个versionKey,因此我可以使用它从Versions映射中查找版本并在表单元格中输出该值。
在模板中,我可以从范围中使用$environment和$service变量,但是我无法计算Go模板语法来创建versionKey结构。
这是省略了标记的模板代码:
{{$environments := .Environments}}
{{$services := .Services}}
{{$versions := .Versions}}
{{range $service := $services}}
...
{{range $environment := $environments}}
...
{{index $versions ...? }} // How to create versionKey struct map key here?
...
{{end}}
...
{{end}}
仅使用模板代码就不能。您需要执行的Go代码提供某种支持才能做到这一点。按照设计原则,模板不应包含复杂的逻辑。您可能会争论这是否复杂,但是模板语法对此不提供支持。
最简单的解决方案是在templateData结构中添加一个Version()方法,该方法将简单地返回给定环境和服务的版本:
func (t *templateData) Version(environment, service string) string {
return t.Versions[versionKey{
Environment: environment,
Service: service,
}]
}
在模板中使用它:
{{range $service := $services -}}
{{range $environment := $environments}}
{{$environment}} - {{$service}} version: {{$.Version $environment $service}}
{{end}}
{{end}}
测试它:
t := template.Must(template.New("").Parse(templ))
td := &templateData{
Environments: []string{"EnvA","EnvB"},
Services: []string{"ServA","ServB"},
Versions: map[versionKey]string{
{"EnvA","ServA"}:"1.0.0",
{"EnvA","ServB"}:"1.0.1",
{"EnvB","ServA"}:"1.0.2",
},
}
if err := t.Execute(os.Stdout, td); err != nil {
panic(err)
}
输出(在Go Playground上尝试):
EnvA - ServA version: 1.0.0
EnvB - ServA version: 1.0.2
EnvA - ServB version: 1.0.1
EnvB - ServB version:
备择方案
代替templateData.Version()方法,您可以轻松地注册一个函数,该函数可以从给定的环境和服务创建并返回类型为versionKey的值。有关详细信息,请参见Template.Funcs()。但是,这将更加复杂,但由于可以在其他地方重用而更加灵活。在此处查看以下示例:Golang模板(以及将funcs传递到模板)。对此的微小变化是将函数值作为任何其他模板数据传递,而不是将其注册为可调用的命名函数。
另一种替代方法是将您的Versions字段"转换"为地图,例如:
Versions map[string]map[string]string
首先可以由环境索引,然后由服务索引,您可以在模板中通过2个{{index}}动作实现索引。您将不得不检查第一个索引是否产生任何结果。
很棒的答案,非常感谢。 出于兴趣,为什么对templateData使用指针?
@JohnTopley两者(指针和非指针接收器)都可以在此简单示例中工作。 我完全出于习惯使用了指针接收器,如果示例演变为一个更复杂的方法,其中方法还需要更改/修改接收器,那么我不必回头将接收器更改为指针接收器。
我了解-谢谢。