dongyuan9149 2015-02-23 12:48
浏览 243
已采纳

在Go模板范围循环中,每次循环都会在循环外部声明的变量重置吗?

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?

  • 写回答

2条回答 默认 最新

  • dsdfd2322 2015-02-23 13:19
    关注

    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}}
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 Stata 面板数据模型选择
  • ¥20 idea运行测试代码报错问题
  • ¥15 网络监控:网络故障告警通知
  • ¥15 django项目运行报编码错误
  • ¥15 请问这个是什么意思?
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services
  • ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏
  • ¥15 模糊pid与pid仿真结果几乎一样
  • ¥15 java的GUI的运用