问题
In Go's database/sql package, there are a bunch of Null[Type] structs that help map database values (and their possible nulls) into code. I'm trying to figure out how to test whether a struct field is null, or in other words, when its Valid property is false.
The recommended way to print a SQL field is to use the .Value property, like this:
<div>{{ .MyStruct.MyField.Value }}</div>
This works great.
But suppose I have something slightly more complicated, where I need to test the value against something else, for example:
<select name="y">
{{ range .SomeSlice }}
<option value="{{ . }}" {{ if eq $.MyStruct.MyField.Value .}}selected="selected"{{ end }}>{{ . }}</option>
{{ end }}
</select>
As it happens, this works great, too, unless .MyField is not Valid, in which case I get the error, "error calling eq: invalid type for comparison". The error makes sense, because Go can't compare a nil Field against another value (or something like that).
I would have thought the 'easy' solution would be to test first whether the Value is nil, and then compare it against what I need, like this:
<select name="y">
{{ range .SomeSlice }}
<option value="{{ . }}" {{ if and ($.MyStruct.MyField) (eq $.MyStruct.MyField.Value .)}}selected="selected"{{ end }}>{{ . }}</option>
{{ end }}
</select>
In this case, I get the same "error calling eq: invalid type for comparison". I guess that means .MyField "exists" even though the value of .MyField is not Valid. So, then I tried a half dozen other versions, mostly with the same error, for example:
<select name="y">
{{ range .SomeSlice }}
<option value="{{ . }}" {{ if and ($.MyStruct.MyField.Valid) (eq $.MyStruct.MyField.Value .)}}selected="selected"{{ end }}>{{ . }}</option>
{{ end }}
</select>
At this point, I'm realizing I really don't understand how to test for the existence of a valid field at all. I'd appreciate any help you might have.
Thanks.
回答1:
The and
function in Go templates is not short-circuit evaluated (unlike the &&
operator in Go), all its arguments are evaluated always. Quoting from text/template package doc:
and Returns the boolean AND of its arguments by returning the first empty argument or the last argument, that is, "and x y" behaves as "if x then y else x". All the arguments are evaluated.
This means that the {{if}}
action of yours:
{{ if and ($.MyStruct.MyField) (eq $.MyStruct.MyField.Value .)}}
Even though the condition would be evaluated to false
if $.MyStruct.MyField
is nil
, but eq $.MyStruct.MyField.Value .
will also be evaluated and result in the error you get.
Instead you may embed multiple {{if}}
actions, like this:
{{if $.MyStruct.MyField}}
{{if eq $.MyStruct.MyField.Value .}}selected="selected"{{end}}
{{end}}
You may also use the {{with}}
action, but that also sets the dot, so you have to be careful:
<select name="y">
{{range $idx, $e := .SomeSlice}}
<option value="{{.}}" {{with $.MyStruct.MyField}}
{{if eq .Value $e}}selected="selected"{{end}}
{{end}}>{{.}}</option>
{{end}}
</select>
Note:
You were talking about nil
values in your question, but the sql.NullXX
types are structs which cannot be nil
. In which case you have to check its Valid
field to tell if its Value()
method will return you a non-nil
value when called. It could look like this:
{{if $.MyStruct.MyField.Valid}}
{{if eq $.MyStruct.MyField.Value .}}selected="selected"{{end}}
{{end}}
来源:https://stackoverflow.com/questions/51165642/golang-template-and-testing-for-valid-fields