Go, AppEngine: How to structure templates for application
One of my favorite features of Go is the ability to easily add handlers inside of packages. This greatly simplifies the processes of writing modular code.
For Example:
File Structure
|-- app.yaml
|-- app
| +-- http.go
|-- templates
| +-- base.html
+-- github.com
+-- storeski
+-- appengine
|-- products
| |-- http.go
| +-- templates
| |-- list.html
| +-- detail.html
+-- account
|-- http.go
+-- templates
|-- overview.html
+-- notifications.html
Each packages has a http.go file that takes ownership of a url prefix. For example the products
package under github.com/storeski/appengine/products
would own any inbound url starting with /products
.
With this modular approach it is beneficial to store the templates within the products
package. If you would like to maintain a consistant base template for the site you can establish a convention where you extend templates/base.html
.
Example
templates/base.html
<!DOCTYPE HTML>
<html>
<head>
<title>{{.Store.Title}}</title>
</head>
<body>
<div id="content">
{{template "content" .}}
</div>
</body>
</html>
github.com/storeski/appengine/products/templates/list.html
{{define "content"}}
<h1> Products List </h1>
{{end}}
github.com/storeski/appengine/products/http.go
func init() {
http.HandleFunc("/products", listHandler)
}
var listTmpl = template.Must(template.ParseFiles("templates/base.html",
"github.com/storeski/appengine/products/templates/list.html"))
func listHandler(w http.ResponseWriter, r *http.Request) {
tc := make(map[string]interface{})
tc["Store"] = Store
tc["Products"] = Products
if err := listTmpl.Execute(w, tc); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
This approach is very exciting because it makes the sharing of apps/package trivial. If I write a package that handles authentication which takes ownership of the /auth
url. Any developer that, then, adds the package to their product root instantly has all of the functionality. All they have to do is create a base template (templates/base.html
) and direct their users to /auth
.