htmlgo

HTML Partials in GoLang


I am just getting started with Go, and I want to create a web app with it. What I am trying now is to use the templating in a handlebarsjs-esque manner. I want to pull my header and footer out of my main page so I can inject them on every webpage.

My current set up is supposed to parse the homepage, header, and footer HTML files and cache them. Then I execute my home.html template, which has fields for the pages Title, the header.html file, and the footer.html file.

Whenever I search for similar pages I just see javascript pages, so if this is a repost, let me know where to look.

edit: I have updated my code to take tips from the answers by @Minty and @putu. I am trying to read the html files and store them in a data map, while also adding the template define to my templates. There are some new bugs that I am working on squashing, so the site does not render currently. But, if there are any new tips you can give, that would help a lot.

server.go

package main

import (
    "html/template"
    "io/ioutil"
    "net/http"
    "regexp"
)

var tPath = "./temps/"
var dPath = "./data/"

var templates = template.Must(template.ParseFiles(tPath+"home.html", dPath+"header.html", dPath+"footer.html"))
var validPath = regexp.MustCompile("^/")

func rootHandler(wr http.ResponseWriter, req *http.Request) {
    title := "home"
    headerFile, headErr := ioutil.ReadFile(dPath + "header.html")
    footerFile, footErr := ioutil.ReadFile(dPath + "footer.html")

    if headErr != nil || footErr != nil {
        http.Error(wr, headErr.Error(), http.StatusInternalServerError)
        http.Error(wr, footErr.Error(), http.StatusInternalServerError)
    }

    data := map[string]interface{}{
        "Title":  title,
        "Header": string(headerFile),
        "Footer": string(footerFile),
    }

    err := templates.ExecuteTemplate(wr, title+".html", data)

    if err != nil {
        http.Error(wr, err.Error(), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc("/", rootHandler)
    http.ListenAndServe(":8080", nil)
}

home.html:

{{define "homeHTML"}}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>{{.Title}} - MySite</title>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        {{.Header}}
        <h1>Welcome!</h1>
        {{.Footer}}
    </body>
</html>
{{end}}

Header.html:

{{define "headerHTML"}}
<header>
    <h1>MySite</h1>
    <br>
    <nav>
        <a href="/">Home</a>
    </nav>
</header>
{{end}}

Footer.html

{{define "footerHTML"}}
<footer>
    <p>Thank You for Visiting</p>
</footer>
{{end}}

Solution

  • This is full working example.

    home.html inside temps folder:

    {{define "homeHTML"}}
    
    {{template "headHTML" .}}
    
    {{template "headerHTML" .}}
    
    {{template "footerHTML" .}}
    
    {{end}}
    

    head.html inside data folder:

    {{define "headHTML"}}
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>{{.title}} - MySite</title>
            <link rel="stylesheet" type="text/css" href="style.css">
        </head>  
    {{end}}
    

    header.html inside data folder

    {{define "headerHTML"}}
    <header>
        {{.header}}
        <h1>Welcome to my site!</h1>
        <br>
        <nav>
            <a href="/">Home</a>
        </nav>
    </header>
    {{end}}
    

    footer.html inside data folder:

    {{define "footerHTML"}}
    <h1>Welcome! {{.footer}}</h1>
    <footer>
    <p>Thank You for Visiting</p>
    </footer>
    </body>
    </html>
    {{end}}
    

    And the code will be like this

    package main
    
    import (
        "html/template"
        "io/ioutil"
        "net/http"
        "path/filepath"
        "strings"
    )
    
    var tPath = "./temps/"
    var dPath = "./data/"
    
    var templateDirs = []string{"temps", "data"}
    var templates *template.Template
    
    func getTemplates() (templates *template.Template, err error) {
        var allFiles []string
        for _, dir := range templateDirs {
            files2, _ := ioutil.ReadDir(dir)
            for _, file := range files2 {
                filename := file.Name()
                if strings.HasSuffix(filename, ".html") {
                    filePath := filepath.Join(dir, filename)
                    allFiles = append(allFiles, filePath)
                }
            }
        }
    
        templates, err = template.New("").ParseFiles(allFiles...)
        return
    }
    
    func init() {
        templates, _ = getTemplates()
    }
    
    func rootHandler(wr http.ResponseWriter, req *http.Request) {
        title := "home"
    
        data := map[string]interface{}{
            "title":  title,
            "header": "My Header",
            "footer": "My Footer",
        }
    
        err := templates.ExecuteTemplate(wr, "homeHTML", data)
    
        if err != nil {
            http.Error(wr, err.Error(), http.StatusInternalServerError)
        }
    }
    
    func main() {
        http.HandleFunc("/", rootHandler)
        http.ListenAndServe(":8080", nil)
    }