在Go模板范围循环中,是在每次迭代时在循环重置之外声明的变量吗?

Del*_*nam 6 go go-templates

我正在尝试使用在Go模板范围循环外声明的变量来查看上一篇文章是否与当前帖子发生在同一天.这是一个简化的例子.

.Posts一组帖子结构在哪里,每个结构都有一个.Content和一个.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 }}
Run Code Online (Sandbox Code Playgroud)

问题是$prevDate似乎""在循环的每次迭代开始时重置为.

任何人都可以帮助我理解为什么$prevDate在每次迭代时重置值,并且可能建议一种方法来完成我在这里尝试做的事情?

icz*_*cza 10

注意: Go 1.11将支持通过赋值修改模板变量.这将是有效的代码:

{{ $v := "init" }}
{{ if true }}
  {{ $v = "changed" }}
{{ end }}
v: {{ $v }} {{/* "changed" */}}
Run Code Online (Sandbox Code Playgroud)

原始答案预先约会Go 1.11如下:


变量不会重置.基本上发生的是你$prevDate在循环中重新声明变量.但它仅在重新声明之后和结束{{end}}标记之前的范围内{{range}}.所以当循环的下一个迭代出现时,你只看到你没有改变的"外部"变量(因为你创建了一个新变量).

您无法更改您创建的模板变量的值.

你可以做的是例如使用以下range形式:

{{ range $index, $post := .Posts }}
Run Code Online (Sandbox Code Playgroud)

和...

解决方案#1:具有注册功能

您可以为模板注册一个函数(请参阅参考资料template.Funcs())$index,然后它将返回前一个元素的日期字段(at $index -1).

它看起来像这样:

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))
Run Code Online (Sandbox Code Playgroud)

从您的模板中,您可以将其称为:

{{range $index, $post := .Posts}}
    {{$prevDate := PrevDate $index}}
{{end}}
Run Code Online (Sandbox Code Playgroud)

解决方案#2:使用帖子方法

这个解决方案是模拟的,但更简单:为您添加一个方法Posts,您可以直接调用它.无需注册功能.

例如:

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
}
Run Code Online (Sandbox Code Playgroud)

从您的模板中,您可以将其称为:

{{range $index, $post := .Posts}}
    {{$prevDate := $.Posts.PrevDate $index}}
{{end}}
Run Code Online (Sandbox Code Playgroud)

  • @DelPutnam 不,你不能修改模板变量,你只能重新声明它们,在很多情况下这很好(你仍然可以用同名引用它),但你必须知道所有(重新)声明都是_scoped_,它们从(重新)声明到当前块结束一直有效。其理念是 _complex_ 事物不应成为模板的一部分,而应在 Go 中实现(并从模板访问或传递给模板)。是的,您可能会争辩说更改变量的值并不复杂,但它不会改变您无法修改它们的事实。 (2认同)