如何修改Golang嵌套模板中的内容?

如何修改Golang嵌套模板中的内容? 我有一个主布局,想要在其中填充多个子页面。同时我还尝试动态更改这些内容。

代码按预期运行,但我无法更改内容。无论传递什么模板,内容都保持不变。似乎总是获取第一个匹配项。即使使用硬编码的文件名也不起作用。

主布局

{{define "layout"}}
    <html>
    <body>
	    {{ template "content" }}
    </body>
    </html>
{{end}}

子模板1 - index.gohtml

{{ define "content" }}

<h1 style="color: red;">Page 1!</h1>

{{ end }}

子模板2 - about.gohtml

{{ define "content" }}

<h1 style="color: blue;">Page 2!</h1>

{{ end }}

Go代码

package main

import (
	"html/template"
	"net/http"
	"strings"
)

var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
	http.HandleFunc("/", index)
	http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {

	path := strings.Trim(r.URL.Path, "/")
	switch path {
	case "":
		path = ("index.gohtml")
	default:
		path = (path + ".gohtml")
	}

	err := tpl.ExecuteTemplate(w, "layout", path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

我也尝试过在执行前使用ParseFiles,但没有成功。有什么建议可以解决这个问题吗?


更多关于如何修改Golang嵌套模板中的内容?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

更多关于如何修改Golang嵌套模板中的内容?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的。一个布局包含多个不同的模板。

这段代码可以正常工作:

var tpl *template.Template

func init() {tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
   http.HandleFunc("/", index)
   http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
   tpl.ExecuteTemplate(w, "layout", nil)
}

但是当我尝试设置其他内容时:

var tpl *template.Template

func init() {tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
   http.HandleFunc("/", index)
   http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
   tpl, _ = template.ParseFiles("layout.gohtml", "index.gohtml")
   tpl.ExecuteTemplate(w, "layout", nil)
}

出现了大量错误:

http: panic serving 127.0.0.1:52020: runtime error: invalid memory address or nil pointer dereference etc

所以欢迎提供任何建议!

终于我让它工作了,但前提是不按照手册操作。

解决方案第一部分

在模板中跳过 {{define…}} 和 {{end}}。很奇怪…

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web Programming</title>
  </head>
  <body>
    layout level
    {{ template "content" . }}
  </body>
</html>

在子模板中也是如此…

<p>subpage</p>

解决方案第二部分

我找到了一个使用 AddParseTree 的代码片段,以下是代码(简化版,无错误处理)

package main

import (
    "html/template"
    "net/http"
    "strings"
)

var tpl *template.Template

func init() {
    tpl = template.Must(template.ParseGlob("templates/*.html"))
}

func main() {
    http.HandleFunc("/", index)
    http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {

    path := strings.Trim(r.URL.Path, "/")
    switch path {
    case "":
        path = ("home.html")
    default:
        path = (path + ".html")
    }

    layout := tpl.Lookup("layout.html")
    layout, _ = layout.Clone()
    t := tpl.Lookup(path)
    _, _ = layout.AddParseTree("content", t.Tree)
    layout.Execute(w, "")

我真的不明白为什么必须违背手册才能让它正常工作。任何能够启发我的评论都将不胜感激。

问题在于你的代码中使用了错误的模板执行方法。ExecuteTemplate 的第三个参数应该是数据对象,而不是模板名称。你需要修改执行逻辑来正确选择子模板。

以下是修正后的代码:

Go代码

package main

import (
	"html/template"
	"net/http"
	"strings"
)

var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
	http.HandleFunc("/", index)
	http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
	path := strings.Trim(r.URL.Path, "/")
	if path == "" {
		path = "index"
	}

	// 重新解析模板以确保获取最新的模板定义
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
	
	// 直接执行对应的内容模板
	err := tpl.ExecuteTemplate(w, path, nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

或者,如果你想要保持布局结构,可以使用关联模板的方法:

package main

import (
	"html/template"
	"net/http"
	"strings"
)

func main() {
	http.HandleFunc("/", index)
	http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
	path := strings.Trim(r.URL.Path, "/")
	if path == "" {
		path = "index"
	} else {
		path = path + ".gohtml"
	}

	// 动态解析布局和对应的内容模板
	tpl := template.Must(template.ParseFiles("templates/layout.gohtml", "templates/"+path))
	
	err := tpl.ExecuteTemplate(w, "layout", nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

另一个更简洁的解决方案是使用模板组合:

package main

import (
	"html/template"
	"net/http"
	"strings"
)

var tpl *template.Template

func init() {
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
}

func main() {
	http.HandleFunc("/", index)
	http.ListenAndServe(":8080", nil)
}

func index(w http.ResponseWriter, r *http.Request) {
	path := strings.Trim(r.URL.Path, "/")
	var templateName string
	
	switch path {
	case "":
		templateName = "index"
	case "about":
		templateName = "about"
	default:
		templateName = "index"
	}

	// 重新解析所有模板
	tpl = template.Must(template.ParseGlob("templates/*.gohtml"))
	
	err := tpl.ExecuteTemplate(w, templateName, nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

主要问题是模板解析和执行逻辑不正确。第一个解决方案直接执行内容模板,第二个解决方案动态组合布局和内容模板,第三个解决方案通过重新解析确保模板定义是最新的。

回到顶部