Переменные внутри шаблонов в golang

Каково пространство имен переменных внутри html/text шаблонов? Я думал, что переменная $x может изменить значение внутри шаблона, но этот пример показывает мне, что я не могу.

Я потерпел неудачу, когда попытался сгруппировать турниры по годам - примерно так (http://play.golang.org/p/EX1Aut_ULD):

package main

import (
    "fmt"
    "os"
    "text/template"
    "time"
)

func main() {
    tournaments := []struct {
        Place string
        Date  time.Time
    }{
        // for clarity - date is sorted, we don't need sort it again
        {"Town1", time.Date(2015, time.November, 10, 23, 0, 0, 0, time.Local)},
        {"Town2", time.Date(2015, time.October, 10, 23, 0, 0, 0, time.Local)},
        {"Town3", time.Date(2014, time.November, 10, 23, 0, 0, 0, time.Local)},
    }
    t, err := template.New("").Parse('
{{$prev_year:=0}}
{{range .}}
    {{with .Date}}
        {{$year:=.Year}}
                    {{if ne $year $prev_year}}
                        Actions in year {{$year}}:
                {{$prev_year:=$year}}
            {{end}}
    {{end}}

        {{.Place}}, {{.Date}}
    {{end}}

    ')
    if err != nil {
        panic(err)
    }
    err = t.Execute(os.Stdout, tournaments)
    if err != nil {
        fmt.Println("executing template:", err)
    }
}

Ответ 1

Изменение: см. fooobar.com/questions/1233292/... для более актуального ответа.


Оригинальный ответ:

https://golang.org/pkg/text/template/#hdr-Variables:

Область действия переменной распространяется на действие "end" структуры управления ("if", "with" или "range"), в которой она объявлена, или до конца шаблона, если такой структуры управления нет.

Таким образом, $prev_year вы определили с помощью {{$prev_year:=$year}} действует только до... следующей строки ({{end}}).

Кажется, нет способа обойти это.

"Правильный" способ сделать это - убрать эту логику из шаблона и выполнить группировку в коде Go.

Вот рабочий пример: https://play.golang.org/p/DZoSXo9WQR

package main

import (
    "fmt"
    "os"
    "text/template"
    "time"
)

type Tournament struct {
    Place string
    Date  time.Time
}

type TournamentGroup struct {
    Year        int
    Tournaments []Tournament
}

func groupTournamentsByYear(tournaments []Tournament) []TournamentGroup {
    if len(tournaments) == 0 {
        return nil
    }

    result := []TournamentGroup{
        {
            Year:        tournaments[0].Date.Year(),
            Tournaments: make([]Tournament, 0, 1),
        },
    }

    i := 0
    for _, tournament := range tournaments {
        year := tournament.Date.Year()
        if result[i].Year == year {
            // Add to existing group
            result[i].Tournaments = append(result[i].Tournaments, tournament)
        } else {
            // New group
            result = append(result, TournamentGroup{
                Year: year,
                Tournaments: []Tournament{
                    tournament,
                },
            })
            i++
        }
    }

    return result
}

func main() {
    tournaments := []Tournament{
        // for clarity - date is sorted, we don't need sort it again
        {"Town1", time.Date(2015, time.November, 10, 23, 0, 0, 0, time.Local)},
        {"Town2", time.Date(2015, time.October, 10, 23, 0, 0, 0, time.Local)},
        {"Town3", time.Date(2014, time.November, 10, 23, 0, 0, 0, time.Local)},
    }

    t, err := template.New("").Parse('
{{$prev_year:=0}}
{{range .}}
    Actions in year {{.Year}}:
    {{range .Tournaments}}

            {{.Place}}, {{.Date}}
    {{end}}
    {{end}}

    ')
    if err != nil {
        panic(err)
    }
    err = t.Execute(os.Stdout, groupTournamentsByYear(tournaments))
    if err != nil {
        fmt.Println("executing template:", err)
    }
}

Ответ 3

Как упоминалось в ответе this, область действия этой переменной "повторное назначение" заканчивается блоком {{end}}. Поэтому, используя стандартные переменные только, проблема не решена и она должна быть решена внутри программы Go, выполняющей шаблон.

В некоторых рамках, однако, это не так просто (например, protoc-gen-gotemplate).

Библиотека Sprig добавляет дополнительные функциональные возможности стандартного языка шаблонов. Один из них - изменчивые карты, которые можно использовать следующим образом:

// init the dictionary (you can init it without initial key/values as well)
{{$myVar := dict "key" "value"}}

// getting the "key" from the dictionary (returns array) and then fetching the first element from that array
{{pluck "key" $myVar | first}}

// conditional update block
{{if eq "some" "some"}}
     // the $_ seems necessary because Go template functions need to return something
     {{$_ := set $myVar "key" "newValue"}}
{{end}}

// print out the updated value
{{pluck "key" $myVar | first}}

Этот маленький пример печатает:

value
newValue

Прагматический подход состоял бы в том, чтобы использовать один словарь для всех изменяемых переменных и хранить их под своим соответствующим именем переменной в качестве ключа.

Ссылка: