Go to template / html iteration to generate table from struct

Given a set of structures, how can I use the "range" template iterator to print a table that assigns a row for each structure and a column for a field value without explaining the field names?

container := []Node type Node struct { Contact_id int Employer_id int First_name string Middle_name string Last_name string } 

Template Code:

 {{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}} 

When I try to iterate with

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

I was told that I can not iterate over the values. Is there any clean way to do this?

+8
iterator reflection html go templates
source share
1 answer

With html/template you cannot iterate over fields in a structure. 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 structure. Or you need:

  • use an intermediate type, for example. [][]interface{} , as the container variable that you pass to the template
  • enter each cell separately as shown below.
  • create a template function that converts the struct values ​​to some type that you can iterate through

Since the structure is defined at compile time and will not change its structure at run time, iteration is not needed and will not make things clearer in the template. I would advise doing this.

Edit

But sometimes thinking is good. Branden also pointed out that you can let the iteration range over the value returned by the function. If you use reflection, this will be the easiest approach.

Full working example using the 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 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 } 

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

+11
source share

All Articles