dshun123456 2013-07-07 04:59
浏览 53
已采纳

Go模板:是否可以嵌套范围?

This one is seemingly simple but it's driving me insane.

How does one go about referencing a struct element higher in the scope within a nested range in golang templates?

Example:

type Foo struct {
  Id string
  Name string
}

type Bar struct {
  Id string
  Name string
}

var foos []Foo
var bars []Bar

// logic to populate both foos and bars

In the template:

{{range .foos}}
  <div>Foo {{.Name}}</div>
  <div>
    {{range ..bars}}
      <div>Bar {{.Name}} <input type="text" name="ids_{{..Id}}_{{.Id}}" /></div>
    {{end}}
  </div>
{{end}}

Obviously ..bars and ..Id don't work, but hopefully my intent is clear. I'd like to iterate through all combinations of Foo and Bar and generate a form element with a name build by both the Foo's Id and the Bar's Id.

The problem is that it seems it is impossible to:

  1. Access bars from inside the scope of the foos range scope
  2. Access Foo's Id from inside the bar's range scope

I have a temporary workaround to this by putting a bunch of redundant fields in both structs, but this seems very ugly to me, violates DRY, and in general feels very wrong.

Is there any way with golang templates to do what I'd like to do?

  • 写回答

1条回答 默认 最新

  • douruduan8812 2013-07-07 05:43
    关注

    Yes. I feel as if not finding a solution comes from not reading the text/template package closely enough. If you are using html/template, the syntax is the same (and they tell you to read text/template ;)). Here is a complete working solution for what you might want to do.

    Go file:

    package main
    
    import (
        "bytes"
        "io/ioutil"
        "os"
        "strconv"
        "text/template"
    )
    
    type Foo struct {
        Id   string
        Name string
    }
    
    type Bar struct {
        Id   string
        Name string
    }
    
    var foos []Foo
    var bars []Bar
    
    func main() {
        foos = make([]Foo, 10)
        bars = make([]Bar, 10)
    
        for i := 0; i < 10; i++ {
            foos[i] = Foo{strconv.Itoa(i), strconv.Itoa(i)} // just random strings
            bars[i] = Bar{strconv.Itoa(10 * i), strconv.Itoa(10 * i)}
        }
    
        tmpl, err := ioutil.ReadFile("so.tmpl")
        if err != nil {
            panic(err)
        }
    
        buffer := bytes.NewBuffer(make([]byte, 0, len(tmpl)))
    
        output := template.Must(template.New("FUBAR").Parse(string(tmpl)))
        output.Execute(buffer, struct {
            FooSlice []Foo
            BarSlice []Bar
        }{
            FooSlice: foos,
            BarSlice: bars,
        })
    
        outfile, err := os.Create("output.html")
        if err != nil {
            panic(err)
        }
        defer outfile.Close()
        outfile.Write(buffer.Bytes())
    }
    

    Note: You can probably do something to not load the file into an intermediate buffer (use ParseFiles), I just copied and pasted some code that I had written for one of my projects.

    Template file:

    {{ $foos := .FooSlice }}
    {{ $bars := .BarSlice }}
    
    {{range $foo := $foos }}
      <div>Foo {{$foo.Name}}</div>
      <div>
        {{range $bar := $bars}}
          <div>Bar {{$bar.Name}} <input type="text" name="ids_{{$foo.Id}}_{{$bar.Id}}" /></div>
        {{end}}
      </div>
    {{end}}
    

    The two morals of this story are
    a) use variables in templates judiciously, they are beneficial
    b) range in templates also can set variables, you do not need to rely solely on $ or .

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 在获取boss直聘的聊天的时候只能获取到前40条聊天数据
  • ¥20 关于URL获取的参数,无法执行二选一查询
  • ¥15 液位控制,当液位超过高限时常开触点59闭合,直到液位低于低限时,断开
  • ¥15 marlin编译错误,如何解决?
  • ¥15 有偿四位数,节约算法和扫描算法
  • ¥15 VUE项目怎么运行,系统打不开
  • ¥50 pointpillars等目标检测算法怎么融合注意力机制
  • ¥20 Vs code Mac系统 PHP Debug调试环境配置
  • ¥60 大一项目课,微信小程序
  • ¥15 求视频摘要youtube和ovp数据集