In a Go template range loop, are variables declared outside the loop reset on each iteration?

go, go-templates

Solution

Note: Go 1.11 will support modifying template variables via assignment. This will be valid code:

{{ $v := "init" }}
{{ if true }}
  {{ $v = "changed" }}
{{ end }}
v: {{ $v }} {{/* "changed" */}}

Original answer pre-dating Go 1.11 follows:

Variables are not reset. Basically what happens is that you redeclare the `$prevDate` variable inside the loop. But it is only in scope after the redeclaration and before the closing `{{end}}` tag of the `{{range}}`. So when the next iteraiton of the loop comes, you only see the "outer" variable which you haven't changed (because you created a new).

You can't change the values of the template variables you create.

What you can do is for example use the following `range` form:

{{ range $index, $post := .Posts }}

And...

Solution #1: with a registered Function

And you can register a function for the template (see `template.Funcs()`) to which you can pass the `$index` and it would return the date field of the previous element (at `$index -1`).

It would look something like this:

func PrevDate(i int) string {
    if i == 0 {
        return ""
    }
    return posts[i-1].Date
}

// Registering it:
var yourTempl = template.Must(template.New("").
    Funcs(map[string]interface{}{"PrevDate": PrevDate}).
    Parse(yourStringTemplate))

And from your template you can call it like:

{{range $index, $post := .Posts}}
    {{$prevDate := PrevDate $index}}
{{end}}

Solution #2: with a Method of Posts

This solution is analog but is even simpler: add a method to your `Posts` and you can call it directly. No need to register a function.

For example:

type Post struct {
    // Your Post type
    Date string
}

type Posts []Post

func (p *Posts) PrevDate(i int) string {
    if i == 0 {
        return ""
    }
    return (*p)[i-1].Date
}

And from your template you can call it like:

{{range $index, $post := .Posts}}
    {{$prevDate := $.Posts.PrevDate $index}}
{{end}}

Problem

I'm trying to use a variable declared outside a Go template range loop to see if the previous post occurred on the same day as the current post. Here's a simplified example. Where `.Posts` is an array of post structs that each have a `.Content` and a `.Date`. ``` {{ $prevDate := "" }} {{ range $post := .Posts }} {{ if ne $prevDate $post.Date }} <div class="post-date">Posts dated: {{ $post.Date }}</div> {{ end }} <div class="post-content">{{ $post.Content }}</div> {{ $prevDate := $post.Date }} {{ end }} ``` The problem is that `$prevDate` seems to be reset to `""` at the start of each iteration of the loop. Can anyone help me understand why the value of `$prevDate` is reset on each iteration and perhaps suggest a way to accomplish what I'm trying to do here?

Original source