Golang中如何通过主模板传递数据到子模板?

Golang中如何通过主模板传递数据到子模板? 我已经成功地将数据传递给了一层模板。

main.go

	path := strings.Trim(r.URL.Path, "/")
	page = page + ".html"
    list := Get(query)
	err := tpl.ExecuteTemplate(w, page, list)

posts.html

<!DOCTYPE html>
<html data-theme="classic" lang="en">

  <head>
    {{template "httphead"}}
  </head>
  <body>
    <div class="content">
      <table>
        <col style="width:50px">
        <col style="width:100%">
        {{range .}}
        <tr>
          <td><img src="avatar/{{.Id}}.jpg"></td>
          <td>
            <h4>#{{.Id}} {{.Subject}}</h4>
            <p>John Doe</p>
          </td>
        </tr>
        {{end}}
      </table>
    </div>
  </body>
</html>

但是,在使用主题时,这种方法不起作用。由于每个模板都有自己的 <html data-theme="classic">,因此在设置主题时会出现延迟。

在线示例:http://94.237.92.101:2020/posts (这个可以工作,但主题设置延迟)

所以现在我尝试创建一个主HTML模板。

layout.html

<!DOCTYPE html>
<html data-theme="" lang="en">
  {{template "httphead"}}

  <body>
    <div class="mainbox">
      {{template "header"}}
      <div class="contentbox">
        {{template "content" .}}
      </div>
    </div>
  </body>

</html>

并为数据创建单独的子模板:

posts.html

<div class="contentbox">
    <div class="content">
      <table>
        <col style="width:50px">
        <col style="width:100%">
        {{range .}}
        <tr>
          <td><img src="avatar/{{.Id}}.jpg"></td>
          <td>
            <h4>#{{.Id}} {{.Subject}}</h4>
          </td>
        </tr>
        {{end}}
      </table>
    </div>
  </div>
</div>

main.go 中:

page = page + ".html"

layout := tpl.Lookup("layout.html")
layout, _ = layout.Clone()
t := tpl.Lookup(page)
_, _ = layout.AddParseTree("content", t.Tree)
layout.Execute(w, page, list)  <--- 无法传递数据

./main.go:72:16: layout.Execute 调用参数过多 实际参数:(http.ResponseWriter, string, interface {}) 期望参数:(io.Writer, interface {})

在线示例 http://94.237.92.101:3030/posts (没有传递数据)

  1. 如何通过主模板传递数据?
  2. 有没有更好的方法来实现这个?

更多关于Golang中如何通过主模板传递数据到子模板?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

Sibert:

如何通过主模板传递数据?

您是想将模板名称作为第二个参数传递吗?

您是否尝试过使用 Template.ExecuteTemplate 而不是 Template.Execute

更多关于Golang中如何通过主模板传递数据到子模板?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


lutzhorn:

你是否尝试过使用 Template.ExecuteTemplate 而不是 Template.Execute

解决方案比我想象的要简单:

layout.Execute(w, list)

http://94.237.92.101:3030/posts

lutzhorn:

你是否尝试过使用 Template.ExecuteTemplate 而不是 Template.Execute

layout.ExecuteTemplate(w, page, list)

只渲染子模板。数据被传递了。

tpl.ExecuteTemplate(w, page, list)

只渲染主模板。没有数据被传递。

我该如何同时渲染主模板和子模板?

在Go模板中,通过主模板传递数据到子模板的正确方式是使用模板执行时的数据参数。以下是解决方案:

1. 修复主模板的数据传递

main.go 中的问题在于 Execute 方法调用参数错误:

// 错误的方式
layout.Execute(w, page, list)  // 参数过多

// 正确的方式
layout.Execute(w, list)  // 只传递数据和writer

2. 完整的解决方案

main.go:

func handler(w http.ResponseWriter, r *http.Request) {
    path := strings.Trim(r.URL.Path, "/")
    page := path + ".html"
    
    // 获取数据
    list := Get(query)
    
    // 查找主模板
    layout := tpl.Lookup("layout.html")
    if layout == nil {
        http.Error(w, "Layout template not found", http.StatusInternalServerError)
        return
    }
    
    // 克隆模板以避免数据竞争
    layout, err := layout.Clone()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    // 查找内容模板
    contentTmpl := tpl.Lookup(page)
    if contentTmpl == nil {
        http.Error(w, "Content template not found", http.StatusInternalServerError)
        return
    }
    
    // 将内容模板添加到主模板
    _, err = layout.AddParseTree("content", contentTmpl.Tree)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    // 正确执行模板,只传递数据和writer
    err = layout.Execute(w, list)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
}

3. 使用模板定义的完整示例

layout.html:

<!DOCTYPE html>
<html data-theme="{{.Theme}}" lang="en">
<head>
    {{template "httphead" .}}
</head>
<body>
    <div class="mainbox">
        {{template "header" .}}
        <div class="contentbox">
            {{template "content" .}}
        </div>
    </div>
</body>
</html>

posts.html:

{{define "content"}}
<div class="content">
    <table>
        <col style="width:50px">
        <col style="width:100%">
        {{range .Posts}}
        <tr>
            <td><img src="avatar/{{.Id}}.jpg"></td>
            <td>
                <h4>#{{.Id}} {{.Subject}}</h4>
                <p>{{.Author}}</p>
            </td>
        </tr>
        {{end}}
    </table>
</div>
{{end}}

4. 使用结构体传递复杂数据

main.go:

type PageData struct {
    Theme   string
    Title   string
    Posts   []Post
    User    User
}

type Post struct {
    Id      int
    Subject string
    Author  string
}

func handler(w http.ResponseWriter, r *http.Request) {
    // 准备数据
    data := PageData{
        Theme: "classic",
        Title: "Posts Page",
        Posts: GetPosts(),
        User:  GetCurrentUser(),
    }
    
    // 执行模板
    err := tpl.ExecuteTemplate(w, "layout.html", data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

5. 使用模板组合的替代方案

main.go:

func handler(w http.ResponseWriter, r *http.Request) {
    // 解析所有模板
    tmpl, err := template.ParseFiles(
        "layout.html",
        "header.html",
        "posts.html",
        "httphead.html",
    )
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    // 准备数据
    data := map[string]interface{}{
        "Theme": "classic",
        "Posts": GetPosts(),
    }
    
    // 执行模板
    err = tmpl.ExecuteTemplate(w, "layout.html", data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

6. 使用模板函数传递数据

main.go:

func main() {
    // 创建模板并添加函数
    tmpl := template.New("").Funcs(template.FuncMap{
        "getTheme": func() string {
            return "classic"
        },
    })
    
    // 解析模板
    tmpl = template.Must(tmpl.ParseFiles("layout.html", "posts.html"))
    
    http.HandleFunc("/posts", func(w http.ResponseWriter, r *http.Request) {
        data := struct {
            Posts []Post
        }{
            Posts: GetPosts(),
        }
        
        tmpl.ExecuteTemplate(w, "layout.html", data)
    })
}

layout.html:

<!DOCTYPE html>
<html data-theme="{{getTheme}}" lang="en">
<!-- 模板内容 -->
{{template "content" .}}
</html>

关键点:

  1. Execute 方法只接受两个参数:io.Writerinterface{}(数据)
  2. 使用结构体或map组织数据
  3. 通过 . 在子模板中访问主模板传递的数据
  4. 使用 {{template "name" .}} 中的点号将当前数据上下文传递给子模板
回到顶部