Golang中如何使用base模板文件处理html/template
Golang中如何使用base模板文件处理html/template
func Alluser(w http.ResponseWriter, r *http.Request) {
tpl := template.Must(template.ParseFiles("./tpl/alluser.html", "./tpl/base.html" ))
rows, err := db.Query("SELECT username, email FROM users")
if err != nil {
http.Error(w, "did not select * from users", 500)
return
}
defer rows.Close()
for rows.Next() {
data := new(UserList)
err := rows.Scan(&data.Username, &data.Email)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
obj := ListPageData{
Users: []UserList{
{data.Username, data.Email},
},
}
tpl.ExecuteTemplate(w, "base", obj)
}
if err = rows.Err(); err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
}
// base.html - parent
{{define "base"}}
<!doctype html>
<html lang="en">
..
<main role="main" class="base_main container">
{{template "content" .}}
</main>
</body>
</html>
{{end}}
// child.html
{{define "content"}}
<ul class="list-group list-group-flush">
{{range .Users}}
<li class="list-group-item">
name: {{.Username}}, email: {{.Email}}
</li>
{{end}}
</ul>
{{end}}
使用基础模板时,文档类型声明(<!doctype html>)会与数据({{range .Users}})一起被渲染。
更多关于Golang中如何使用base模板文件处理html/template的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你说得非常对,你的反馈极具价值!一份优秀的入门教程应力求对初学者来说易于理解、清晰明了且自成体系。以下是你所提观点的重要性所在:
复杂度: “入门”教程应聚焦于核心概念,避免用高级细节或参考资料让新用户感到不知所措。
更多关于Golang中如何使用base模板文件处理html/template的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你不需要定义“父”模板。只需要定义子模板。以下是我使用多个子模板的“基础模板”:
<!DOCTYPE html>
<html lang="en">
{{template "httphead"}}
{{template "body" "home"}}
{{template "icn"}} {{template "nav" "home"}}
<main>
{{template "hdr_top"}}
<article>
{{template "mst_home"}}{{template "det_empty"}}
</article>
</main>
{{template "httpend"}}
</body>
</html>
Sibert:
你不需要定义“父”模板。只需要子模板。以下是我使用多个子模板的“基础模板”
实际上问题不在于模板。不过我也修正了它们。
func Alluser(w http.ResponseWriter, r *http.Request) {
tpl := template.Must(template.ParseFiles("./tpl/alluser.html", "./tpl/base.html" ))
rows, err := db.Query("SELECT username, email FROM users")
if err != nil {
http.Error(w, "did not select * from users", 500)
return
}
defer rows.Close()
var albums []*UserList
for rows.Next() {
data := new(UserList)
err := rows.Scan(&data.Username, &data.Email)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
albums = append(albums, data)
fmt.Println("data", data)
}
tpl.ExecuteTemplate(w, "base", albums)
if err = rows.Err(); err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
}
问题的关键在于 for 循环,你必须跳出它。
{{template "./tpl/base.html"}}
{{block "content" .}}
<ul class="list-group list-group-flush">
{{range .}}
<li class="list-group-item">
name: {{.Username}}, email: {{.Email}}
</li>
{{end}}
</ul>
{{end}}
创建一个单独的HTML文件,通常命名为 base.html,其中包含网站的通用布局元素,例如页眉、页脚、导航栏等。在你希望注入每个页面特定内容的位置定义占位符({{define "content"}} 和 {{end}})。
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
</head>
<body>
<header>
<!-- 导航栏等 -->
</header>
<main>
{{template "content" .}}
</main>
<footer>
<!-- 页脚内容 -->
</footer>
</body>
</html>
2. 定义单独的页面模板:
为网站的每个页面创建单独的HTML文件。这些文件只需要定义该页面特定的内容,它们将从基础模板继承布局。
{{define "content"}}
<h1>欢迎来到我的网站!</h1>
<p>这是主页内容。</p>
{{end}}
{{define "content"}}
<h1>关于我</h1>
<p>我叫约翰·多伊,是一名Web开发人员。</p>
{{end}}
3. 解析和执行模板:
在你的Go代码中,使用 html/template 包来解析基础模板和各个页面模板,然后使用数据上下文执行它们:
package main
import (
"html/template"
"net/http"
)
func main() {
// 解析基础模板
baseTemplate, err := template.ParseFiles("base.html")
if err != nil {
panic(err)
}
// 为主页定义一个处理函数
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := map[string]string{"Title": "Home"}
baseTemplate.ExecuteTemplate(w, "base", data)
})
// 类似地为其他页面定义处理函数
// 启动服务器
http.ListenAndServe(":8080", nil)
}
这是一个基本示例,你可以根据需要进行进一步定制。以下是一些额外的建议:
- 你可以使用嵌套模板来进一步模块化你的布局。
- 你可以向模板传递额外的数据以动态定制内容。
- 你可以使用模板函数在模板内执行简单的逻辑。
请记住,在模板中使用用户生成的内容时,要考虑安全漏洞。在将任何输入渲染到HTML之前,对其进行清理和转义非常重要。
在Golang中使用html/template处理基础模板时,需要在子模板中定义{{define "content"}}块,然后在执行时指定正确的模板名称。你的代码存在两个主要问题:
- 模板解析顺序错误:应该先解析基础模板,再解析子模板
- 执行位置错误:
tpl.ExecuteTemplate应该在循环外部执行
以下是修正后的代码:
func Alluser(w http.ResponseWriter, r *http.Request) {
// 正确解析模板:基础模板在前,子模板在后
tpl := template.Must(template.ParseFiles("./tpl/base.html", "./tpl/alluser.html"))
rows, err := db.Query("SELECT username, email FROM users")
if err != nil {
http.Error(w, "did not select * from users", 500)
return
}
defer rows.Close()
// 收集所有用户数据
var users []UserList
for rows.Next() {
data := new(UserList)
err := rows.Scan(&data.Username, &data.Email)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
users = append(users, *data)
}
if err = rows.Err(); err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
// 一次性执行模板渲染
obj := ListPageData{
Users: users,
}
// 执行基础模板,它会自动包含content块
tpl.ExecuteTemplate(w, "base", obj)
}
基础模板base.html保持不变:
{{define "base"}}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
</head>
<body>
<main role="main" class="base_main container">
{{template "content" .}}
</main>
</body>
</html>
{{end}}
子模板alluser.html:
{{define "content"}}
<ul class="list-group list-group-flush">
{{range .Users}}
<li class="list-group-item">
name: {{.Username}}, email: {{.Email}}
</li>
{{end}}
</ul>
{{end}}
如果需要更灵活的模板继承,可以使用{{block}}指令:
<!-- base.html -->
{{define "base"}}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{block "title"}}默认标题{{end}}</title>
</head>
<body>
<header>{{block "header"}}默认头部{{end}}</header>
<main>{{template "content" .}}</main>
<footer>{{block "footer"}}默认底部{{end}}</footer>
</body>
</html>
{{end}}
<!-- alluser.html -->
{{define "title"}}用户列表{{end}}
{{define "header"}}
<h1>用户管理系统</h1>
{{end}}
{{define "content"}}
<ul class="list-group list-group-flush">
{{range .Users}}
<li class="list-group-item">
name: {{.Username}}, email: {{.Email}}
</li>
{{end}}
</ul>
{{end}}
{{define "footer"}}
<p>© 2023 用户管理系统</p>
{{end}}
这样就能正确渲染包含基础模板结构的完整HTML页面,DOCTYPE声明只会出现一次,用户数据会正确显示在content区域内。

