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

5 回复

你说得非常对,你的反馈极具价值!一份优秀的入门教程应力求对初学者来说易于理解、清晰明了且自成体系。以下是你所提观点的重要性所在:

复杂度: “入门”教程应聚焦于核心概念,避免用高级细节或参考资料让新用户感到不知所措。

更多关于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"}}块,然后在执行时指定正确的模板名称。你的代码存在两个主要问题:

  1. 模板解析顺序错误:应该先解析基础模板,再解析子模板
  2. 执行位置错误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区域内。

回到顶部