如何基于表达式在Go模板中有条件地设置变量,如果没有用if语句包装,可能会导致错误

问题描述 投票:13回答:2

我该怎么做这样的事情:

{{ $use_ssl := (ne $.Env.CERT_NAME "") }}

其中$.Env.CERT_NAME可能是nil / undefined。如果它是nil,则会出现此错误:

at <ne $.Env.CERT_NAME "">: error calling ne: invalid type for comparison

注意:我无法控制传入Go模板的对象,因此必须在模板本身内完全解决这个问题。

我试过的

我试图通过首先检查它是否为非空来解决:

{{ $use_ssl := ( ($.Env.CERT_NAME) && (ne $.Env.CERT_NAME "") ) }}

但它给出了这个错误:

unexpected "&" in operand

所以我切换到这个,这在语法上是允许的:

{{ $use_ssl := (and ($.Env.CERT_NAME) (ne $.Env.CERT_NAME "") ) }}

但后来我失去了short-circuit evaluation,我会得到&&运算符,我又回到了这个错误:

at <ne $.Env.CERT_NAME ...>: error calling ne: invalid type for comparison

好吧,我想,如果我不能用一个漂亮的单线和Go doesn't have a ternary operator,那很好,我会做idiomatic Go way,显然是if/else

{{ if $.Env.CERT_NAME }}
  {{ $use_ssl := (ne $.Env.CERT_NAME "") }}
{{ else }}
  {{ $use_ssl := false }}
{{ end }}

但当然我遇到了范围问题,因为if莫名其妙地(或者至少令人讨厌)创建了一个新的变量范围(与我更习惯的Ruby / ERB模板不同),所以显然我甚至不能这样做:

{{ if $.Env.CERT_NAME }}
  {{ $use_ssl := true }}
{{ else }}
  {{ $use_ssl := false }}
{{ end }}
# $use_ssl: {{ $use_ssl }}

现在没有收到此错误:

undefined variable "$use_ssl"

“没有汗水”,我想。我只是在外部范围内声明变量,因此它将具有正确的范围,内部范围仍然可以更改该变量(具有外部范围的变量)。 (顺便说一下,这就是它在Ruby中的工作方式。)

不,显然所有这一切都是创建2个不同的变量,具有相同的名称,但不同的范围(如何混淆!):

{{ $use_ssl := false }}
{{ if $.Env.CERT_NAME }}
  {{ $use_ssl := true }}
  # $use_ssl: {{ $use_ssl }}
{{ else }}
  {{ $use_ssl := false }}
{{ end }}
# $use_ssl: {{ $use_ssl }}

  # $use_ssl: true
# $use_ssl: false

我也试过像这样内联if语句:

{{ $use_ssl := if $.Env.CERT_NAME { true } }}

但是这给出了这个错误:

unexpected <if> in command

显然if语句不能像在Ruby中那样用作Go中的表达式吗?

如果我尝试使用What is the idiomatic Go equivalent of C's ternary operator?中建议的三元运算符的各种替代方法,我也会遇到语法错误,例如:

c := map[bool]int{true: 1, false: 0} [5 > 4]

当然,您可以使用$.something从内部作用域中的外部作用域读取变量,但是如何在内部作用域中设置$.something

如果不是那样,那怎么样?

那么我错过了什么?这是否可以在Go模板中使用?

Go模板(我是他们的新手)似乎非常有限。几乎你在Go中可以做的一切都不可能在模板中完成。但希望有一个解决方法......

http://play.golang.org/p/SufZdsx-1v有一个聪明的解决方法,包括使用{{$hasFemale := cell false}}创建一个新对象,然后使用$hasFemale.Set true设置一个值,但是如何在不访问评估模板的代码(他们调用template.FuncMap)的情况下执行类似的操作?

其他尝试过失败的人

https://groups.google.com/forum/#!topic/golang-nuts/MUzNZ9dbHrg

这是我与Go有关的最大(也是唯一)问题。我根本不明白为什么还没有实现这个功能。

当模板包在通用上下文中使用时(例如,使用任意json文件执行任意模板文件),您可能没有做预先计算的奢侈。

https://github.com/golang/go/issues/10608

这可能是设计的,但如果有办法在条件之外获得更改的$ v,那将是非常好的。

这是我们在Hugo(https://github.com/spf13/hugo)得到的#1模板问题。我们现在添加了一个hack [$.Scratch.Set "v1" 123]来解决它...

ruby go-templates
2个回答
9
投票

Currently working solution

如果更新范围而不是声明变量,可以使用set或使用merge,您可以在if子句之外访问它。

{{- if eq .podKind "core" -}}
{{- $dummy := set . "matchNodePurpose" .Values.scheduling.corePods.nodeAffinity.matchNodePurpose }}
{{- else if eq .podKind "user" -}}
{{- $dummy := set . "matchNodePurpose" .Values.scheduling.userPods.nodeAffinity.matchNodePurpose }}
{{- end -}}

# Will now output what you decided within an if/else if clause
{{- .matchNodePurpose }}

Future solution (Available with Helm 2.10)

我在Helm模板的上下文中遇到了同样的问题,这些模板是预先安装了一些附加功能的模板,例如Sprig

qazxsw poi一个三元运算符被添加到Sprig,及时Helm会让他们解决你和我的问题。

terenary

28 March 2018, in this pr将返回true | ternary "b" "c"

"b"将返回false | ternary "b" "c"


4
投票

我想我可能已经为我的特定问题找到了一个解决方法,但我很想听到其他更普遍解决问题的答案。

来自"c"的这一行:

https://github.com/jwilder/nginx-proxy/blob/1e0b930/nginx.tmpl#L91

这给了我另一个尝试的想法。我想解决方案是使用{{ $default_host := or ($.Env.DEFAULT_HOST) "" }} 的组合并设置额外的临时变量......

or
© www.soinside.com 2019 - 2024. All rights reserved.