gogo-html-template

How can I pass a nested struct to a gohtml template file and display it in a table?


I can't figure out how to pass the data that I get from the MySQL database to my HTML template and display every column in a table.

The Instructions I found work fine, however when i try to implement it into my own code it won't pass the data to my HTML and the table remains empty.

Go code:


var table = NewView("bootstrap", "views/pages/table.gohtml")

func main() {
    http.Handle("/views/", http.StripPrefix("/views/", http.FileServer(http.Dir("./views"))))
    http.HandleFunc("/table", tableHandler)
    fmt.Println("server running on port :8080")
    http.ListenAndServe(":8080", nil)
}
func tableHandler(w http.ResponseWriter, r *http.Request) {
    db := ipSQL.SqlConnect()

    data := ipSQL.SqlSelect(db)
    // This returns a nested struct and stores it in the 'data' variable.
    /*
        type Data struct {
        Items []Devicevalue_view
        }

        type Devicevalue_view struct {
            Name        string
            Ip          string
            Pcn         string
            Description string
        }
    */

    fmt.Println(data)
    //The println prints the following: 'test1: {[{Name1 192.168.166.13 123456 webserver} {Name2 192.168.166.14 123456 webserver2}]}'

    err := table.Render(w, data)
    //This renders the page together with a default layout. table is initialized somewhere else in this way:
    // var table = NewView("bootstrap", "views/pages/table.gohtml") --> This is a function I found and confirmed to be working.
    if err != nil {
        panic(err)
    }
}

I've been using the following code to render a standard layout for my webpage from a different file called bootstrap.gohtml and get the body of the HTML from the file I specify. (Added this in ---UPDATE 2)



func NewView(layout string, files ...string) *View {
    files = append(layoutFiles(), files...)
    t, err := template.ParseFiles(files...)
    if err != nil {
        panic(err)
    }

    return &View{
        Template: t,
        Layout:   layout,
    }
}

type View struct {
    Template *template.Template
    Layout   string
}

func (v *View) Render(w http.ResponseWriter, data interface{}) error {
    spew.Dump(data)
    return v.Template.ExecuteTemplate(w, v.Layout, data)
}

func layoutFiles() []string {
    files, err := filepath.Glob(LayoutDir + "/*.gohtml")
    if err != nil {
        panic(err)
    }
    return files
}


The HTLM template looks like this, note that table.Render uses bootstrap.gohtml in which i call on the 'yield' template which I define in table.gohtml.

{{define "yield"}}
<!DOCTYPE html>
<html lang="en">
    <body>
        <table >
            <tr>
                <th>name</th>
                <th>ip</th>
                <th>pcn</th>
                <th>description</th>
            </tr>
            {{ range .Items}}
            <tr>
                <td>{{ .Name }}</td>
                <td>{{ .Ip }}</td>
                <td>{{ .Pcn }}</td>
                <td>{{ .Description }}</td>
            </tr>
            {{ end}}
        <tr>
            <td>END</td>
        </tr>
    </table>
</body>
</html>
{{end}}

The bootstrap.gohtml looks something like this:

{{define "bootstrap"
   <head>some links to css among other things</head>
   <nav>a navigation bar</nav>
   {{template "yield"}}
   <foot>footer</foot>
{{end}}

I compared my code to a working example and the data needed for the table seems to be correct leaving me to believe the issue lies in getting the variables into the HTML table.

I've tried many other variable names in my html file but it won't add them to the table, the column names work fine just not the contents.

The result I expect is a table with multiple rows that has the data that I've currently stored in the 'data' variable.

---UPDATE 1 I've replaced '{{ range .}}' with '{{ range .Items}}' but it still wont show the data in the table.

---UPDATE 2 Added more of my code to the post.

---UPDATE 3 I've added spew.Dump(data) to the render() function, the output is the following:

server running on port :8080
{[{Name1 192.168.166.13 123456 Webserver1} {Name2 192.168.166.14 123456 Webserver2}]}
(ipSQL.Data) {
 Items: ([]ipSQL.Devicevalue_view) (len=2 cap=2) {
  (ipSQL.Devicevalue_view) {
   Name: (string) (len=5) "Name1",
   Ip: (string) (len=14) "192.168.166.13",
   Pcn: (string) (len=6) "123456",
   Description: (string) (len=10) "Webserver1"
  },
  (ipSQL.Devicevalue_view) {
   Name: (string) (len=5) "Name2",
   Ip: (string) (len=14) "192.168.166.14",
   Pcn: (string) (len=6) "123456",
   Description: (string) (len=10) "Webserver2"
  }
 }
}

Solution

  • I got it working by changing {{template "yield"}} to {{template "yield" .}}. From what I understand of it this will used the pipeline to parse the variables to the next template file.

    {{define "bootstrap"
       <head>some links to css among other things</head>
       <nav>a navigation bar</nav>
       {{template "yield" .}}
       <foot>footer</foot>
    {{end}}
    

    The yield template now has access to the variables I parsed to the bootstrap template.