Skip to content Skip to sidebar Skip to footer

Go Template/html Iteration To Generate Table From Struct

Given a collection of structs, how can I use the 'range' template iterator to print out a table that assigns a row per struct, and a column per field value without explicity naming

Solution 1:

With the html/template, you cannot iterate over the fields in a struct. In the documentation for the package, you can read:

{{range pipeline}} T1 {{end}} The value of the pipeline must be an array, slice, map, or channel.

That is, Pipeline cannot be a struct. Either you need to:

  • use an intermediate type, eg. [][]interface{}, as container variable that you pass to the template
  • type out each cell separately as you've shown
  • create a template function that converts struct values to some type you can iterate over

Since a struct is defined at compile-time and won't change its structure during runtime, iteration is not necessary and wouldn't make things more clear in the template. I would advise against it.

Edit

But sometimes reflection is a good thing. Brenden also pointed out that you can actually let range iterate over the value returned from a function. If using reflection, this would be the easiest approach.

Full working example using a template function:

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 overvar htmlTemplate = `{{range .}}<tr>
{{range rangeStruct .}} <td>{{.}}</td>
{{end}}</tr>
{{end}}`funcmain() {
    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 structfuncRangeStructer(args ...interface{}) []interface{} {
    iflen(args) == 0 {
        returnnil
    }

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

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

    return out
}

Output:

<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

Post a Comment for "Go Template/html Iteration To Generate Table From Struct"