How to get a field by index in template? - go

I send a slice of articles into template. Each articlestruct is like:
type Article struct {
ID uint32 `db:"id" bson:"id,omitempty"`
Content string `db:"content" bson:"content"`
Author string `db:"author" bson:"author"`
...
}
I can loop over articles slice in a {{range $n := articles}} and get each {{$n.Content}} but what I want is to have only the first one (outside the range loop) to use in headline.
What I tried is:
{{index .articles.Content 0}}
But I get:
Template File Error: template: articles_list.tmpl:14:33: executing
"content" at <.articles.Content>: can't evaluate field Content in type
interface {}
If I just invoke
{{index .articles 0}}
It shows the whole article[0] object.
How can I fix this?

The index function access the nth element of the specified array, so writing
{{ index .articles.Content 0 }}
is essentially trying to write articles.Content[0]
You would want something akin to
{{ with $n := index .articles 0 }}{{ $n.Content }}{{ end }}

A more concise way is:
{{(index .articles.Content 0).Content }}
Which would be the equivalent of articles[0].Content.

{{(index .articles 0).Content}}

Related

How do you parse brackets from path string on a variable return in Hugo?

I am writing a layout template for a Hugo generated post/page. The .yaml header is
image:
- post/mytitle/image.jpg
The template incorporates the variable in Hugo as
{{ .Params.Image }}
When served, the variable is returned as
[post/mytitle/image.jpg]
My html then becomes
<img src="[post/mytitle/image.jpg]"/>
which is 404 in the browser. I've tried a number of Hugo functions to no avail like {{ trim .Param.Image "[]" }} and {{ subset .Params.Image 1 -1 }} and {{ print .Params.Image }}. Each time Hugo returned the error: "error calling substr: unable to cast []string{"post/mytitle/image.jpg"} of type []string to string".
How do I get the variable to return the string without the brackets, or alternatively, how do I omit the brackets from the string?
In Go template, you access an item in a slice with index:
{{ index .Params.Image 0 }}
The question is why the value is a sequence in the first place. You could simply do
image:
post/mytitle/image.jpg
Then you could keep the original syntax since it is now a simple value, not a sequence.
If you want to possibly include multiple images, you'd do
{{ range .Params.Image }}<img src="{{.}}">{{ end }}
Then you can have
image:
- post/mytitle/image.jpg
- post/mytitle/anotherimage.jpg

How to set a golang variable to a template variable

I am new to golang and want to understand how to assign a template variable in golang using the golang variable.
I am using go-swagger to generate go code.
Below is the piece of a customized template to generate my swagger client.
func demo() {
{{range .Operations}}
Value := main.CheckAvail(*{{.Package}}.{{ pascalize .Name }})
{{$value := .Value}}
{{if $value }}
{{ pascalize .Name }}
{{end}}
{{end}}
}
But it gives me the error that:
" <.Value>: can't evaluate field Value in type generator.GenOperation ".
How do I assign the value to the $value? Any help?
This is not possible. The template file is a wire frame for a go file. It just generates a file of go code, which is not executed yet.
All in {{ ... }} is evaluated, when the template is parsed. At this point, all text outside the curly braces is just plain text. After the (successful) execution of the template generation the generated text will be picked up by the go compiler and compiled (if it is correct go code).
In this case you cannot evaluate Value.

How to have short circuit with and/or in text/template

I have this Go template:
{{ if and $b.Trigger $b.Trigger.Name }}
Name is {{ $b.Trigger.Name }}.
{{ else }}
...other stuff...
{{ end }}
I'm trying to get this template to do:
if b.Trigger != nil && $b.Trigger.Name != "" { ...
however it doesn't work, because as text/template godoc says, both arguments to and/or functions are evaluated.
When the $b.Trigger.Name is evaluated, it errors out because $b.Trigger can be nil. So it returns error:
template: builds.html:24:46: executing "content" at <$b.Trigger.Name>: can't evaluate field Name in type *myType
I tried refactoring this to:
{{ if and (ne $b.Trigger nil) (ne $b.Trigger.Name "") }}
and weird enough, this fails as well, it says I can't compare $b.Trigger with nil, which doesn't make sense because that field is a pointer type:
template: builds.html:24:31: executing "content" at <ne $b.Trigger nil>: error calling ne: invalid type for comparison
Any ideas?
As Volker noted above, nest the ifs:
{{if $b.Trigger}}{{if $b.Trigger.Name}}
Name is {{ $b.Trigger.Name }}.
{{end}}{{end}}
Or, more succinctly:
{{with $b.Trigger}}{{with .Name}}
Name is {{.}}.
{{end}}{{end}}
Unfortunately, the above won't handle else clauses. Here's one (rather ugly) possibility:
{{$bTriggerName := ""}}
{{with $b.Trigger}}{{$bTriggerName = .Name}}{{end}}
{{with $bTriggerName}}
Name is {{.}}.
{{else}}
...other stuff...
{{end}}
I looked to see if gobuffalo/plush could do this more elegantly, but as of 2019-04-30 it cannot.
Here explains why all arguments are evaluated in a pipeline.
It is evaluated through go template functions
https://golang.org/pkg/text/template/#hdr-Functions

Golang: Ordering map by slice in Go templates

I have a question about how to order a map by a slice in Go Templates and if it is possible.
Problem: I have a slice of ordered variable names that I want to display on a website, accompanying them I have a map of metadata of variable information that I would like to display together with the variable.
If I have the following struct that I pass in to the template:
type Data struct {
Variables []string
Information map[string]int
}
I would iterate over the slice and pass the variable name in to the map
{{ range $v := .Variables }} {{ index .Information $v }} {{ end }} // Doesn't work.
Here's a Go Playground with the example.
https://play.golang.org/p/AL2csnXdoU
Question: How can I do this?
I am fairly new to Golang. Thankful for any input.
The following should work. To access .Information inside range, you should use $, which is basically d in your Playground example.
{{ range .Variables }} {{ index $.Information . }} {{ end }}

Golang separating items with comma in template

I am trying to display a list of comma separated values, and don't want to display a comma after the last item (or the only item if there is only one).
My code so far:
Equipment:
{{$equipment := .Equipment}}
{{ range $index, $element := .Equipment}}
{{$element.Name}}
{{if lt $index ((len $equipment) -1)}}
,
{{end}}
{{end}}
The current output: Equipment: Mat , Dumbbell ,
How do I get rid of the trailing comma
A nice trick you can use is:
Equipment:
{{$equipment := .Equipment}}
{{ range $index, $element := .Equipment}}
{{if $index}},{{end}}
{{$element.Name}}
{{end}}
This works because the first index is 0, which returns false in the if statement. So this code returns false for the first index, and then places a comma in front of each following iteration. This results in a comma separated list without a leading or trailing comma.
Add a template function to do the work for you. strings.Join is perfect for your use case.
Assuming tmpl contains your templates, add the Join function to your template:
tmpl = tmpl.Funcs(template.FuncMap{"StringsJoin": strings.Join})
Template:
Equipment:
{{ StringsJoin .Equipment ", " }}
Playground
Docs: https://golang.org/pkg/text/template/#FuncMap
Another way to skin the cat, if you can create a new type for the field.
type MyList []string
func (list MyList) Join() string {
return strings.Join(list, ", ")
}
Then you can use the function in the template like regular:
{{ .MyList.Join }}
without index
it can be useful for any iteration, like on maps that do not have index:
{{ $first := true }}
{{ range $_, $element := .}}
{{if not $first}}, {{else}} {{$first = false}} {{end}}
{{$element.Name}}
{{end}}
Playground
If you are willing to use an external library, it seems like sprig library has a "join" function (see here):
join
Join a list of strings into a single string, with the given separator.
list "hello" "world" | join "_"
The above will produce hello_world
join will try to convert non-strings to a string value:
list 1 2 3 | join "+"
The above will produce 1+2+3

Resources