Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang template and testing for Valid fields

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.

like image 803
Brent Avatar asked Jul 04 '18 04:07

Brent


1 Answers

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}}
like image 63
icza Avatar answered Sep 17 '22 23:09

icza