I'm trying to get my head around embedding templates using html/template
in Go. I very much like the logic-less template design and I have confidence in its ability to safely escape things as expected (sometimes other template libs get this wrong).
I am, however, having a bit of a problem trying to implement a little helper to render my templates in my HTTP handlers based on the "final" template name. My base.tmpl is effectively "standard" across all of my pages, and in cases where it isn't I can set {{ template checkoutJS }}
in base.tmpl and add some per-page JS by setting {{ define checkoutJS }}https://path.to/extra.js {{ end }}
.
I want to be able to say renderTemplate(w, "template_name.tmpl", data)
in my HTTP handlers, where data
is a map[string]interface{} containing strings or structs with whatever I wish to fill in.
Here's the code so far:
base.tmpl
{{ define "base" }}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ template "title" . }}</title>
</head>
<body>
<div id="sidebar">
...
</div>
{{ template "content" }}
<div id="footer">
...
</div>
</body>
</html>
create_listing.tmpl
{{ define "title" }}Create a New Listing{{ end }}
{{ define "content" }}
<form>
...
</form>
{{ end }}
login_form.tmpl
{{ define "title" }}Login{{ end }}
{{ define "content" }}
<form>
...
</form>
{{ end }}
main.go
package main
import (
"fmt"
"github.com/gorilla/mux"
"html/template"
"log"
"net/http"
)
// Template handling shortcuts
var t = template.New("base")
func renderTemplate(w http.ResponseWriter, tmpl string, data map[string]interface{}) {
err := t.ExecuteTemplate(w, tmpl, data)
// Things will be more elegant than this: just a placeholder for now!
if err != nil {
http.Error(w, "error 500:"+" "+err.Error(), http.StatusInternalServerError)
}
}
func monitorLoginForm(w http.ResponseWriter, r *http.Request) {
// Capture forms, etc.
renderTemplate(w, "login_form.tmpl", nil)
}
func createListingForm(w http.ResponseWriter, r *http.Request) {
// Grab session, re-populate form if need be, generate CSRF token, etc
renderTemplate(w, "create_listing.tmpl", nil)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/monitor/login", monitorLoginForm)
http.Handle("/", r)
log.Fatal(http.ListenAndServe(":8000", nil))
}
func init() {
fmt.Println("Starting up.")
_, err := t.ParseGlob("templates/*.tmpl")
if err != nil {
log.Fatal("Error loading templates:" + err.Error())
}
}
This compiles, but I get back an empty response from my handler. Note that I don't have a route for the second handler: that code is there just to show how I want to call renderTemplate()
from handlers.