如何在 Go 的 html/template 中正确传递数据给子模板

在 go 中使用 `html/template` 时,若主模板通过 `{{template "name"}}` 调用子模板,默认会以 `nil` 数据执行子模板;必须显式传入当前上下文(如 `{{template "name" .}}`)才能让子模板访问结构体字段。

Go 的模板系统采用“作用域隔离”设计:每个 {{template}} 动作默认以空上下文(nil)执行被调用模板,除非显式指定数据源。这正是你遇到 {{.Title}} 和 {{.Page.Test}} 渲染为空的原因——{{template "article"}} 没有传入任何数据,子模板内部的 . 为 nil,字段访问自然失败。

✅ 正确做法是在 core.tmpl 中改为:

{{template "article" .}}

这样,当前执行上下文(即 *News 实例)会被完整传递给 article.tmpl,其中 {{.Title}} 和 {{.Page.Test}} 均可正常解析。

完整可运行示例:

package main

import (
    "html/template"
    "os"
    "path/filepath"
)

type Page struct {
    Test string
}

type News struct {
    Page
    Title string
}

func main() {
    // 假设模板文件位于当前目录
    t, err := template.ParseFiles("core.tmpl", "article.tmpl")
    if err != nil {
        panic(err)
    }

    p := &News{
        Title: "Go Template Deep Dive",
        Page: Page{
            Test: "From Embedded Struct",
        },
    }

    err = t.Execute(os.Stdout, p)
    if err != nil {
        panic(err)
    }
}

对应模板文件:

core.tmpl:




{{template "article" .}} 

article.tmpl:

{{define "article"}}

{{.Title}}

{{.Page.Test}}

{{end}}

⚠️ 注意事项:

  • . 是模板中的当前数据上下文,必须显式传递才能跨模板共享;
  • 若子模板需独立数据(如侧边栏、分页组件),可传入特定字段或新结构体:{{template "sidebar" .SidebarData}};
  • 使用 template.ParseGlob 或 template.New("").ParseFiles(...) 可更灵活管理多模板;
  • 所有 {{define}} 模板名必须全局唯一,重复定义将导致 ParseFiles 失败。

掌握 {{template "name" pipeline}} 的数据传递机制,是构建模块化 HTML 模板(如布局+内容分离)的基础前提。