Search code examples
gotemplatesvariablescounter

Golang Templates - Update and Print Counter Variable


I am trying to update and print a counter variable inside of a Go template when iterating through a nested for loop.

I am not trying to print the index of either of my data structures as I loop through. I am not going to print every object in each data structure, and I want this counter variable to only increment when an equality statement is true.

What am I doing wrong here?

Go Playground link: https://play.golang.org/p/RsuEk1PqZ7a

type a struct {
    Numbers []string
    Letters []string
}

var data = &a{
    Numbers: []string{"one", "two"},
    Letters: []string{"a","b","b", "c"},
}

var tmplSrc = `start
{{with $i := 0}}
  {{range $number := .Numbers}}
    {{range $letter := .Letters}}
      {{if eq $letter "b"}}
        {{$i = add $i 1}}
        {{$i}}
      {{end}}
    {{end}}
  {{end}}
{{end}}
fin
`

func main() {
    funcMap := template.FuncMap{
        "add": func(a int, b int) int {
            return a + b
        },
    }
    tmpl := template.Must(template.New("test").Funcs(funcMap).Parse(tmplSrc))
    tmpl.Execute(os.Stdout, data)
}

Solution

  • The error returned from tmpl.Execute execute hints at the problem:

    test:3:21: executing "test" at <.Numbers>: can't evaluate field Numbers in type int

    Always handle errors!

    The problem is that {{with $i := 0}} sets . to the 0. The template expects . to be the data argument to the template. Fix by using $ to refer to the data argument. A similar change is required for .Letters because {{range}} also sets ..

    {{with $i := 1}}
      {{range $number := $.Numbers}}
        {{range $letter := $.Letters}}
        {{if eq $letter "b"}}
          {{$i = add $i 1}}
          {{$i}}
        {{end}}
        {{end}}
      {{end}}
    {{end}}
    

    I used {{with $i := 1}} to match the code in the playground. The question uses {{with $i := 0}}. The code in the question introduces yet another problem: the contents of the {{with}} is skipped because the condition evaluates to false (0 is false in template conditions). Fix by removing the {{with}} directive. It's not needed.

    {{$i := 0}}
    {{range $number := $.Numbers}}
      {{range $letter := $.Letters}}
        {{if eq $letter "b"}}
          {{$i = add $i 1}}
            {{$i}}
         {{end}}
      {{end}}
    {{end}}