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.
Update: The following answer predates Go 1.18 where the argument evaluation for and
and or
template function changed to stop early if the results is know. The answer is only valid for prior Go versions.
Starting with Go 1.18, the following doc states the changed behavior:
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." Evaluation proceeds through the arguments left to right and returns when the result is determined.
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}}