type Post struct {
User
Body string
}
// IndexViewModel struct
type IndexViewModel struct {
Title string
User
Posts []Post
}
<html>
<head>
{{if .Title}}
<title>{{.Title}} - blog</title>
{{else}}
<title>Welcome to blog!</title>
{{end}}
</head>
<body>
<h1>Hello, {{.Username}}!</h1>
{{range .Posts}}
<div><p>{{ .Username }} says: <b>{{ .Body }}</b></p></div>
{{end}}
</body>
</html>
package main
import (
"html/template"
"io/ioutil"
"net/http"
"os"
)
// User struct
type User struct {
Username string
}
// Post struct
type Post struct {
User
Body string
}
// IndexViewModel struct
type IndexViewModel struct {
Title string
User
Posts []Post
}
// PopulateTemplates func
// Create map template name to template.Template
func PopulateTemplates() map[string]*template.Template {
const basePath = "templates"
result := make(map[string]*template.Template)
layout := template.Must(template.ParseFiles(basePath + "/_base.html"))
dir, err := os.Open(basePath + "/content")
if err != nil {
panic("Failed to open template blocks directory: " + err.Error())
}
fis, err := dir.Readdir(-1)
if err != nil {
panic("Failed to read contents of content directory: " + err.Error())
}
for _, fi := range fis {
func() {
f, err := os.Open(basePath + "/content/" + fi.Name())
if err != nil {
panic("Failed to open template '" + fi.Name() + "'")
}
defer f.Close()
content, err := ioutil.ReadAll(f)
if err != nil {
panic("Failed to read content from file '" + fi.Name() + "'")
}
tmpl := template.Must(layout.Clone())
_, err = tmpl.Parse(string(content))
if err != nil {
panic("Failed to parse contents of '" + fi.Name() + "' as template")
}
result[fi.Name()] = tmpl
}()
}
return result
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
u1 := User{Username: "bonfy"}
u2 := User{Username: "rene"}
posts := []Post{
Post{User: u1, Body: "Beautiful day in Portland!"},
Post{User: u2, Body: "The Avengers movie was so cool!"},
}
v := IndexViewModel{Title: "Homepage", User: u1, Posts: posts}
templates := PopulateTemplates()
templates["index.html"].Execute(w, &v)
})
http.ListenAndServe(":8888", nil)
}
<html>
<head>
{{if .Title}}
<title>{{.Title}} - blog</title>
{{else}}
<title>Welcome to blog!</title>
{{end}}
</head>
<body>
<div>Blog: <a href="/">Home</a></div>
{{template "content" .}}
</body>
</html>
{{define "content"}}
<h1>Hello, {{.User.Username}}!</h1>
{{range .Posts}}
<div><p>{{ .User.Username }} says: <b>{{ .Body }}</b></p></div>
{{end}}
{{end}}
由于没有像 Jinja2 这样的原生支持模板继承,这个实现的关键就是 PopulateTemplates 函数,它的作用是 遍历 templates/content/ 文件夹下的所有文件,并和 templates/_base.html 合成 template.Template,然后再存入 map 中(在 Python 中一般叫 dict),可以使用例如 index.html 的 key 来访问。