Implement a for loop inside a Go template
Asked Answered
L

3

27

I am working in Go, and right now I need to print at least 20 options inside a select, so I need to use some kind of loop that goes from 0 to 20 (to get an index).

How can I use a for loop inside a Go template?

I need to generate the sequence of numbers inside the template. I don't have any array to iterate.

EDIT: I need to get something like this:

<select>
     <option value="1">1</option>
     <option value="2">2</option>
     <option value="3">3</option>
     <option value="4">4</option>
</select>

So, I need to do in the code something like:

<select>
     {{for i := 1; i < 5; i++}}
        <option value="{{i}}">{{i}}</option>
     {{end}}
</select>

But, this doesn't work.

Laureenlaurel answered 5/8, 2017 at 20:9 Comment(0)
G
29

You can use range in templates as well. See https://golang.org/pkg/text/template/#hdr-Variables

Easiest option might be to just use a slice containing your options:

func main() {
    const tmpl = `
<select>
{{range $val := .}}
     <option value="{{$val}}">{{$val}}</option>
{{end}}
</select>
`
    t := template.Must(template.New("tmpl").Parse(tmpl))

    t.Execute(os.Stdout, []int{1, 2, 3})
}
Ginni answered 5/8, 2017 at 20:19 Comment(1)
yes, I already saw it. But as I understood, range is only to iterate over already created arrays, I don't have any array.I need is to generate the sequence on runtimeLaureenlaurel
R
16

Your best bet is to add an "Iterate" function to your func_map.

template.FuncMap{
        "Iterate": func(count *uint) []uint {
            var i uint
            var Items []uint
            for i = 0; i < (*count); i++ {
                Items = append(Items, i)
            }
            return Items
        },
}

Once you register your function map with text/template, you can iterate like this:

{{- range $val := Iterate 5 }}
  {{ $val }}
{{- end }}

I don't know why this useful function isn't part of the default set of functions text/template provides. Maybe they'll add something similar in the future.

Robby answered 29/8, 2018 at 21:35 Comment(4)
I like your solution because I am using a FuncMap already but I get can't handle 5 for arg of type *uint. I'm new to programming (especially with pointers) and am not sure how to get around this other than to declare count as a type uint not *uint. What would you do to make it work?Perales
@Perales you can using type assertion. Below worked for me ` func iterate(count int) []uint { var i uint var Items []uint for i = 0; i < uint(count); i++ { Items = append(Items, i) } return Items }`Eck
@Perales is there any chance the value you're passing in might be of type string? You might need an additional strtoi-like function.Robby
I disappointingly do not recall what the solution was. The project was fleeting and I haven't touched Go for quite a while now! I can't seem to pull myself away from Python :)Perales
H
0

You can also use a channel to avoid building a slice:

funcMap := template.FuncMap{
    "loop": func(from, to int) <-chan int {
        ch := make(chan int)
        go func() {
            for i := from; i <= to; i++ {
                ch <- i
            }
            close(ch)
        }()
        return ch
    },
}

then:

{{range $x := loop 3 9 }}
Hey {{$x}}!
{{- end}}

See the full working example: https://go.dev/play/p/DP2WuROnCC9

Horrid answered 7/11, 2022 at 10:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.