如何在 Go 中将模板呈现为多个布局?
How to render templates to multiple layouts in Go?
我需要将模板呈现为不同类型的布局。这是我的目录结构。
myapp
|
│ main.go
│
├───static
│ script.js
│ style.css
│
└───templates
│ page1.tmpl
│ page2.tmpl
│ page3.tmpl
│ page4.tmpl
│ page5.tmpl
│
└───layouts
base1.tmpl
base2.tmpl
base3.tmpl
我已经将模板渲染到单个布局模板,但是我无法让它在多个布局上工作。这是我到目前为止得到的:
package main
import (
"html/template"
"net/http"
"fmt"
"github.com/urfave/negroni"
"github.com/oxtoacart/bpool"
"path/filepath"
"log"
)
var (
templates map[string]*template.Template
bufpool *bpool.BufferPool
)
func main() {
loadTemplates()
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
renderTemplate(w, "page1.tmpl",nil)
})
n := negroni.New()
n.Use(negroni.NewLogger())
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
tmpl, ok := templates[name]
if !ok {
return fmt.Errorf("The template %s does not exist.", name)
}
buf := bufpool.Get()
defer bufpool.Put(buf)
err := tmpl.ExecuteTemplate(buf, "base1.tmpl", data)
if err != nil {
return err
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
buf.WriteTo(w)
return nil
}
func loadTemplates() {
if templates == nil {
templates = make(map[string]*template.Template)
}
tmplDir := "templates/"
layouts, err := filepath.Glob(tmplDir + "layouts/*.tmpl")
if err != nil {
log.Fatal(err)
}
includes, err := filepath.Glob(tmplDir + "*.tmpl")
if err != nil {
log.Fatal(err)
}
for _, include := range includes {
files := append(layouts, include)
templates[filepath.Base(include)] = template.Must(template.ParseFiles(files...))
}
fmt.Print(includes)
bufpool = bpool.NewBufferPool(64)
}
下面是 base1.tmpl
的样子:
{{define "base1"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{block "title" .}}{{end}}</title>
</head>
<body>
{{template "content" .}}
</body>
</html>
{{end}}
下面是 page1.tmpl
的样子:
{{define "title"}}Page 1{{end}}
{{define "content"}}
<p>Page 1 contents</p>
{{end}}
我通常采用渲染两次的方法,一次用于内容,一次用于布局,这使您可以在任何布局中使用任何内容,并将该决定推迟到运行时。如果其他人以不同的方式做,会对其他方法感兴趣,但这目前对我有用。
所以使用您发布的代码,在处理程序中是这样的:
data := map[string]interface{}{
"title": "hello world",
}
renderTemplate(w, "base1.tmpl", "page1.tmpl", data)
...
在 renderTemplate 中传递布局和模板,并且:
// Render the template 'name' with data
buf := bufpool.Get()
err := tmpl.ExecuteTemplate(buf, name, data)
if err != nil {
return err
}
// Set the content as a key on data (set as html as it is rendered)
data["content"] = template.HTML(buf.Bytes())
bufpool.Put(buf)
// Render the layout 'layout' with data, using template as content key
buf = bufpool.Get()
defer bufpool.Put(buf)
err = tmpl.ExecuteTemplate(buf, layout, data)
if err != nil {
return err
}
布局:
<html>
<body>
<h1>Base 1</h1>
{{.content}}
</body>
</html>
页数:
<h2>{{.title}}</h2>
<h3>Page 1</h3>
这里是 link 完整代码:
我需要将模板呈现为不同类型的布局。这是我的目录结构。
myapp
|
│ main.go
│
├───static
│ script.js
│ style.css
│
└───templates
│ page1.tmpl
│ page2.tmpl
│ page3.tmpl
│ page4.tmpl
│ page5.tmpl
│
└───layouts
base1.tmpl
base2.tmpl
base3.tmpl
我已经将模板渲染到单个布局模板,但是我无法让它在多个布局上工作。这是我到目前为止得到的:
package main
import (
"html/template"
"net/http"
"fmt"
"github.com/urfave/negroni"
"github.com/oxtoacart/bpool"
"path/filepath"
"log"
)
var (
templates map[string]*template.Template
bufpool *bpool.BufferPool
)
func main() {
loadTemplates()
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
renderTemplate(w, "page1.tmpl",nil)
})
n := negroni.New()
n.Use(negroni.NewLogger())
n.UseHandler(mux)
http.ListenAndServe(":8080", n)
}
func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
tmpl, ok := templates[name]
if !ok {
return fmt.Errorf("The template %s does not exist.", name)
}
buf := bufpool.Get()
defer bufpool.Put(buf)
err := tmpl.ExecuteTemplate(buf, "base1.tmpl", data)
if err != nil {
return err
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
buf.WriteTo(w)
return nil
}
func loadTemplates() {
if templates == nil {
templates = make(map[string]*template.Template)
}
tmplDir := "templates/"
layouts, err := filepath.Glob(tmplDir + "layouts/*.tmpl")
if err != nil {
log.Fatal(err)
}
includes, err := filepath.Glob(tmplDir + "*.tmpl")
if err != nil {
log.Fatal(err)
}
for _, include := range includes {
files := append(layouts, include)
templates[filepath.Base(include)] = template.Must(template.ParseFiles(files...))
}
fmt.Print(includes)
bufpool = bpool.NewBufferPool(64)
}
下面是 base1.tmpl
的样子:
{{define "base1"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{block "title" .}}{{end}}</title>
</head>
<body>
{{template "content" .}}
</body>
</html>
{{end}}
下面是 page1.tmpl
的样子:
{{define "title"}}Page 1{{end}}
{{define "content"}}
<p>Page 1 contents</p>
{{end}}
我通常采用渲染两次的方法,一次用于内容,一次用于布局,这使您可以在任何布局中使用任何内容,并将该决定推迟到运行时。如果其他人以不同的方式做,会对其他方法感兴趣,但这目前对我有用。
所以使用您发布的代码,在处理程序中是这样的:
data := map[string]interface{}{
"title": "hello world",
}
renderTemplate(w, "base1.tmpl", "page1.tmpl", data)
...
在 renderTemplate 中传递布局和模板,并且:
// Render the template 'name' with data
buf := bufpool.Get()
err := tmpl.ExecuteTemplate(buf, name, data)
if err != nil {
return err
}
// Set the content as a key on data (set as html as it is rendered)
data["content"] = template.HTML(buf.Bytes())
bufpool.Put(buf)
// Render the layout 'layout' with data, using template as content key
buf = bufpool.Get()
defer bufpool.Put(buf)
err = tmpl.ExecuteTemplate(buf, layout, data)
if err != nil {
return err
}
布局:
<html>
<body>
<h1>Base 1</h1>
{{.content}}
</body>
</html>
页数:
<h2>{{.title}}</h2>
<h3>Page 1</h3>
这里是 link 完整代码: