【问题标题】:Variables inside templates in golanggolang模板中的变量
【发布时间】:2015-10-08 22:35:52
【问题描述】:

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)
    }
}

【问题讨论】:

  • 通过编写{{$prev_year:=$year}},您正在创建全新的变量。

标签: templates variables go go-templates


【解决方案1】:

在 go1.11 text/template and hence html/template became able to set the value of existing variables 中,这意味着只需对原始代码进行非常小的修改即可使用。

改变

{{$prev_year:=$year}}

{{$prev_year = $year}}

Playground

【讨论】:

  • {{$prev_year:=0}} 之前的 {{range}} 块是关键。变量必须在赋值之前初始化。
【解决方案2】:

编辑:请参阅https://stackoverflow.com/a/52925780/1685538 以获取最新答案。


原答案:

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

变量的作用域延伸到控件的“结束”动作 声明它的结构(“if”、“with”或“range”),或 如果没有这样的控制结构,则模板结束。

所以你用{{$prev_year:=$year}} 定义的$prev_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)
    }
}

【讨论】:

  • 非常感谢,但这也是有问题的。地图我花了一些时间思考,但我没有找到真正惯用的解决方案。在这种情况下,我认为,是最好的回报
  • 非常感谢,但这也是有问题的。模板中的地图以 ASC 顺序呈现,我需要 DESC。因此,我会使用 Less 方法创建一个年份类型。在我的情况下也不需要排序,真实数据来自数据库,因此很容易排序。我花了一些时间思考,但没有找到真正惯用的解决方案。最好的维护可能是 return []struct {PrevYear *int;比赛}
  • @lofcek :你说得对,地图不是你想做的好结构。我添加了一个可以完成这项工作的工作示例
  • @lofcek,看起来 HectorJ 回答了你的问题。你能接受答案吗?
【解决方案3】:

正如this 回答所提到的,该变量“重新分配”的范围以{{end}} 块结束。因此,仅使用标准变量无法解决问题,应该在执行模板的 Go 程序中解决。

然而,在某些框架中,这并不是那么容易(例如 protoc-gen-gotemplate)。

Sprig library 为标准模板语言添加了附加功能。其中之一是可变映射,可以通过以下方式使用:

// 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

一种实用的方法是为所有可变变量使用一个字典,并将它们作为键存储在相应的变量名下。

参考:

【讨论】:

    猜你喜欢
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-22
    • 2019-11-17
    • 2017-10-08
    相关资源
    最近更新 更多