【问题标题】:Go template/html iteration to generate table from structGo template/html 迭代从结构生成表
【发布时间】:2013-11-28 06:12:20
【问题描述】:

给定一个结构集合,我如何使用“范围”模板迭代器打印出一个表,该表为每个结构分配一行,每个字段值一列,而无需明确命名字段?

container := []Node

type Node struct {
    Contact_id        int
    Employer_id       int
    First_name        string
    Middle_name       string
    Last_name         string
}

模板代码:

{{range .container}}

<tr>
<td>{{.Prefix}}</td>
<td>{{.First_name}}</td>
<td>{{.Middle_name}}</td>
<td>{{.Last_name}}</td>

<td>{{.Contact_id}}</td>
<td>{{.Employer_id}}</td>

</tr>
{{end}}

当我尝试使用迭代值时

{{range .container}}
 {{range .}}
 <td>{{.}}</td> 
{{end}}
{{end}}

有人告诉我,我不能迭代这些值。 有什么干净的方法可以做到这一点吗?

【问题讨论】:

  • 您的代码中的.container.Nodes 是什么?如果您想遍历container,只需使用.container
  • 我的错误,我已经更正了上面的例子。

标签: html templates reflection iterator go


【解决方案1】:

使用html/template,您不能迭代结构中的字段。在包的documentation 中,您可以阅读:

{{range pipeline}} T1 {{end}}
管道的值必须是数组、切片、映射或通道。

也就是说,Pipeline 不能是结构体。您需要:

  • 使用中间类型,例如。 [][]interface{},作为您传递给模板的容器变量
  • 按照您的说明分别输入每个单元格
  • 创建一个模板函数,将结构值转换为可以迭代的某种类型

由于结构是在编译时定义的,并且不会在运行时更改其结构,因此不需要迭代,也不会使模板中的内容更加清晰。我建议不要这样做。

编辑

但有时反思是一件好事。 Brenden 还指出,您实际上可以让 range 迭代从函数返回的值。如果使用反射,这将是最简单的方法。

使用模板函数的完整工作示例:

package main

import (
    "html/template"
    "os"
    "reflect"
)

type Node struct {
    Contact_id  int
    Employer_id int
    First_name  string
    Middle_name string
    Last_name   string
}

var templateFuncs = template.FuncMap{"rangeStruct": RangeStructer}

// In the template, we use rangeStruct to turn our struct values
// into a slice we can iterate over
var htmlTemplate = `{{range .}}<tr>
{{range rangeStruct .}} <td>{{.}}</td>
{{end}}</tr>
{{end}}`

func main() {
    container := []Node{
        {1, 12, "Accipiter", "ANisus", "Nisus"},
        {2, 42, "Hello", "my", "World"},
    }

    // We create the template and register out template function
    t := template.New("t").Funcs(templateFuncs)
    t, err := t.Parse(htmlTemplate)
    if err != nil {
        panic(err)
    }

    err = t.Execute(os.Stdout, container)
    if err != nil {
        panic(err)
    }

}

// RangeStructer takes the first argument, which must be a struct, and
// returns the value of each field in a slice. It will return nil
// if there are no arguments or first argument is not a struct
func RangeStructer(args ...interface{}) []interface{} {
    if len(args) == 0 {
        return nil
    }

    v := reflect.ValueOf(args[0])
    if v.Kind() != reflect.Struct {
        return nil
    }

    out := make([]interface{}, v.NumField())
    for i := 0; i < v.NumField(); i++ {
        out[i] = v.Field(i).Interface()
    }

    return out
}

输出:

<tr>
    <td>1</td>
    <td>12</td>
    <td>Accipiter</td>
    <td>ANisus</td>
    <td>Nisus</td>
</tr>
<tr>
    <td>2</td>
    <td>42</td>
    <td>Hello</td>
    <td>my</td>
    <td>World</td>
</tr>

Playground

【讨论】:

  • 这个想法基本上是让应用程序以最少的干预从查询响应转到数据表。我正在将 SQL 查询中的数据直接扫描到结构中,然后反映要在模板中迭代的标题列表以创建 thead。感谢您的回复,我现在可以停止为此而努力了。我要将结构移植到值数组并迭代它们。
  • 然后您可以使用reflect 包来填充您传递给模板的中间[][]interface{} 变量,这与您对标头使用反射的方式非常相似。有时间我会添加一个例子。
  • golang.org/pkg/html/template/#Template.Funcs编写你自己的模板方法rangeStruct,并使用上面提到的reflect。例如:github.com/brendensoares/revel/blob/master/template.go
  • @Brenden 我拒绝了它,因为我认为这是不可能的。试一试,我发现你确实可以让一个模板函数返回一个结构来迭代!这是一个更好的解决方案。我会添加它
  • 太棒了——我敢打赌很多人会觉得这很有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-27
  • 2016-12-25
  • 2014-01-14
  • 2023-01-16
  • 2021-10-09
  • 2016-01-22
相关资源
最近更新 更多