Lars
Lars

Reputation: 1072

How to update field value in a Go template

I have the following case, where I am passing a struct containing a map to a template:

package main

import (
    "log"
    "os"
    "text/template"
)

var fns = template.FuncMap{
    "plus1": func(x int) int {
        return x + 1
    },
}

type codec struct {
    Names map[string]string
    Count int
}

func main() {
    a := map[string]string{"one": "1",
        "two":   "2",
        "three": "3"}

    t := template.Must(template.New("abc").Funcs(fns).Parse(`{{$l := len .Names}}{{range $k, $v := .Names}}{{if ne (plus1 $.Count) $l}}{{$k}} {{$v}} {{end}}{{end}}.`))

    err := t.Execute(os.Stdout, codec{a, 0})
    if err != nil {
        log.Println(err)
    }
}

I would like to increment the Count field of codec so that I can know how many items of the map I've seen.

Upvotes: 1

Views: 1570

Answers (2)

icza
icza

Reputation: 418137

You can simply define a method on your struct:

type codec struct {
    Names map[string]string
    Count int
}

func (c *codec) IncAndGet() int {
    c.Count++
    return c.Count
}

Calling it from a template:

c := &codec{Count: 2}
t := template.Must(template.New("").Parse(`{{.IncAndGet}} {{.IncAndGet}}`))
t.Execute(os.Stdout, c)

Output (try it on the Go Playground):

3 4

Note that for this to work, the method needs a pointer receiver (func (c *codec) IncAndGet()) and you have to pass a pointer to Template.Execute() (c is a pointer in our example: c := &codec{Count: 2}).

If you don't want any result just counting, define it to have a string return type and return the empty string "":

func (c *codec) Inc() string {
    c.Count++
    return ""
}

Upvotes: 1

Not_a_Golfer
Not_a_Golfer

Reputation: 49235

One solution is to make the plus1 function a closure that acts directly on the value of the codec:

// first create a codec instance
c := codec {a, 0}

// now define the function as a closure with a reference to c
fns := template.FuncMap{
  "plus1": func() int {
      c.Count++
      return c.Count
   },
}

// now we don't need to pass anything to it in the template
t := template.Must(template.New("abc").Funcs(fns).Parse(`{{$l := len .Names}}{{range $k, $v := .Names}}{{if ne (plus1) $l}}{{$k}} {{$v}} {{end}}{{end}}.`))

The output was:

one 1 three 3

which I'm guessing is what you were aiming for? And the value is retained in c at the end of execution.

Upvotes: 2

Related Questions