英文:
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}}
英文:
I need to render templates into different kinds of layout. Here's my directory structure.
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
I have done rendering templates to a single layout template but, I can't make it work on multiple layouts. Here's what I got so far:
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)
}
Here's how base1.tmpl
looks like:
{{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}}
And here's how page1.tmpl
looks like:
{{define "title"}}Page 1{{end}}
{{define "content"}}
<p>Page 1 contents</p>
{{end}}
答案1
得分: 0
我通常采用两次渲染的方法,一次用于内容,一次用于布局。这样可以在运行时使用任何内容在任何布局中,并将决策推迟到运行时。如果其他人有不同的方法,我会对其他方法感兴趣,但目前这种方法对我来说是有效的。
根据你发布的代码,可以在处理程序中这样使用:
data := map[string]interface{}{
"title": "hello world",
}
renderTemplate(w, "base1.tmpl", "page1.tmpl", data)
在renderTemplate
函数中,除了传入模板之外,还要传入一个布局:
// 使用数据渲染模板'name'
buf := bufpool.Get()
err := tmpl.ExecuteTemplate(buf, name, data)
if err != nil {
return err
}
// 将内容作为键设置在数据中(设置为HTML,因为它将被渲染)
data["content"] = template.HTML(buf.Bytes())
bufpool.Put(buf)
// 使用模板作为内容键,使用数据渲染布局'layout'
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>
这里是完整代码的链接:
https://play.golang.org/p/R2vr4keZec
英文:
I normally take the approach of rendering twice, once for content, once for layout, this lets you use any content in any layout and defer that decision till runtime. Would be interested in other approaches if other people do it differently, but this is working for me at present.
So using the code you have posted, something like this in handler:
data := map[string]interface{}{
"title": "hello world",
}
renderTemplate(w, "base1.tmpl", "page1.tmpl", data)
...
in renderTemplate pass in a layout as well as a template and:
// 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
}
Layout:
<html>
<body>
<h1>Base 1</h1>
{{.content}}
</body>
</html>
Page:
<h2>{{.title}}</h2>
<h3>Page 1</h3>
Here is a link to full code:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论