Golang中html/template能否实现block继承功能

Golang中html/template能否实现block继承功能 大家好,我认为通过代码来解释我的问题会更清楚:

{{define "base"}}
    <h1>An example template</h1>
    {{block "sidebar" .}}
        <p>My default sidebar content</p>
    {{end}}
{{end}}

<!-- child1.html -->
{{template "base" .}}

<!-- 
    我希望此页面的侧边栏显示:
    <p>My default sidebar content</p>
-->
<!-- 
    答案:什么都不做即可
-->

<!-- child2.html -->

{{template "base" .}}
<!-- 
    我希望此页面显示:
    <p>My default sidebar content</p>
    <p>My custom sidebar content</p>
-->

{{define "sidebar"}}
    <!-- 答案:??? -->
{{end}}

<!-- child3.html -->

{{template "base" .}}
<!-- 
    我希望此页面的侧边栏显示:
    <p>My custom sidebar content</p>
    <p>My default sidebar content</p>
-->

{{define "sidebar"}}
    <!-- 答案:??? -->
{{end}}

我在 Django[1] 和 Laravel[2] 中使用过这个特定功能,想知道如何使用 Go 模板来实现它。

谢谢!

[1] 如何覆盖模板 | Django 文档 | Django [2] Blade 模板 - Laravel - 为 Web 工匠打造的 PHP 框架


更多关于Golang中html/template能否实现block继承功能的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

我之前也阅读了这个帖子——它确实帮助我理解了如何正确排序模板解析,以便覆盖功能正常工作。遗憾的是,我认为它没有涵盖我的具体问题。

更多关于Golang中html/template能否实现block继承功能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


不幸的是,我认为这并没有涵盖我的具体问题。

据我理解,您想要一个基础模板,并在基础模板内为每个页面使用模板。我曾尝试实现这一点,但我放弃了。现在我正在页面级别使用“组件”。这种方法不错,但并非我最初想要的方式。我发现这已经足够好,并且更容易复用组件。

感谢您的建议,但这完全无法让我替换默认的侧边栏,因为它位于 block 之外。我从其他渠道也获得了足够的反馈,确认使用 html/template 没有确切的方法可以实现这一点。我最终采用了这个实现,它已经非常接近我的需求:

Go Playground - The Go Programming Language

我真的很感谢您的帮助!

func main() {
    fmt.Println("hello world")
}

我不太确定我完全理解你想要什么,但类似这样的东西怎么样?

package main

import (
	"fmt"
	"html/template"
	"os"
)

var baseTemplate = `{{define "base"}}
    <h1>An example template</h1>
        <p>My default sidebar content</p>
        {{block "sidebar" .}}
        {{end}}
{{end}}`

var childTemplate = `{{template "base" .}}`

var childTemplateWithCustom = `{{template "base" .}}
{{define "sidebar"}}<p>Custom sidebar template</p>{{end}}`

func main() {
	baseTmpl := template.Must(template.New("baseTemplate").Parse(baseTemplate))

	childTmpl, _ := template.Must(baseTmpl.Clone()).Parse(childTemplate)
	fmt.Println("No custom content:")
	childTmpl.Execute(os.Stdout, nil)

	childTmplWithCustom, _ := template.Must(baseTmpl.Clone()).Parse(childTemplateWithCustom)
	fmt.Println("Custom content:")
	childTmplWithCustom.Execute(os.Stdout, nil)
}

… 其输出为:

No custom content:

    <h1>An example template</h1>
        <p>My default sidebar content</p>
        
        
Custom content:

    <h1>An example template</h1>
        <p>My default sidebar content</p>
        <p>Custom sidebar template</p>

如果你愿意,可以在 Go Playground 上运行它。同时,查看关于与其他模板共享模板构建块的文档:

favicon.ico template package - text/template - Go Packages

Package template implements data-driven templates for generating textual output.

他们称之为“驱动模板”,但其基本思想是可重用的基础模板,你可以通过 .Clone 在其他模板中使用它们。

在Go语言的html/template中,可以通过blockdefine的组合来实现类似Django和Laravel的模板继承功能。以下是针对你三个场景的解决方案:

1. 基础模板 (base.html)

{{define "base"}}
<!DOCTYPE html>
<html>
<body>
    <h1>An example template</h1>
    {{block "sidebar" .}}
        <p>My default sidebar content</p>
    {{end}}
</body>
</html>
{{end}}

2. child1.html - 使用默认侧边栏

{{template "base" .}}
<!-- 
    渲染结果:
    <h1>An example template</h1>
    <p>My default sidebar content</p>
-->

3. child2.html - 追加内容到默认侧边栏

{{define "sidebar"}}
    {{template "base.sidebar" .}}
    <p>My custom sidebar content</p>
{{end}}

{{template "base" .}}
<!-- 
    渲染结果:
    <h1>An example template</h1>
    <p>My default sidebar content</p>
    <p>My custom sidebar content</p>
-->

4. child3.html - 在默认内容前添加自定义内容

{{define "sidebar"}}
    <p>My custom sidebar content</p>
    {{template "base.sidebar" .}}
{{end}}

{{template "base" .}}
<!-- 
    渲染结果:
    <h1>An example template</h1>
    <p>My custom sidebar content</p>
    <p>My default sidebar content</p>
-->

5. Go代码示例

package main

import (
    "html/template"
    "os"
)

func main() {
    // 解析所有模板文件
    tmpl := template.Must(template.ParseFiles(
        "base.html",
        "child1.html",
        "child2.html",
        "child3.html",
    ))
    
    // 执行child2.html
    err := tmpl.ExecuteTemplate(os.Stdout, "child2.html", nil)
    if err != nil {
        panic(err)
    }
}

6. 更复杂的继承示例

<!-- base.html -->
{{define "base"}}
<html>
<head>
    <title>{{block "title" .}}Default Title{{end}}</title>
</head>
<body>
    {{block "content" .}}{{end}}
    {{block "sidebar" .}}
        <div class="default-sidebar">
            <p>Default sidebar</p>
        </div>
    {{end}}
</body>
</html>
{{end}}

<!-- page.html -->
{{define "title"}}Custom Page Title{{end}}

{{define "sidebar"}}
    <div class="custom-sidebar">
        <p>Custom sidebar header</p>
        {{template "base.sidebar" .}}
        <p>Custom sidebar footer</p>
    </div>
{{end}}

{{define "content"}}
    <h1>Page Content</h1>
    <p>This is the main content area.</p>
{{end}}

{{template "base" .}}

关键点:

  1. {{block "name" .}} 定义可被覆盖的区块
  2. {{define "name"}} 在子模板中重新定义区块
  3. {{template "base.sidebar" .}} 引用父模板中的原始内容
  4. 所有模板必须在同一个template.Template实例中解析

这种方式可以实现灵活的模板继承和内容覆盖,虽然语法与Django/Laravel不同,但功能上是等效的。

回到顶部