-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
What version of Go are you using (go version)?
$ go version go version go1.19.2 linux/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
go env Output
$ go env GO111MODULE="on" GOARCH="amd64" GOBIN="" GOCACHE="/home/lijian/.cache/go-build" GOENV="/home/lijian/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="-tags=dev" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/lijian/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/lijian/go" GOPRIVATE="" GOPROXY="https://goproxy.cn,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.19.2" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/home/lijian/Code/blog/go.mod" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3532686189=/tmp/go-build -gno-record-gcc-switches"
What did you do?
To make Go html/template inherit base template properly, every time executing an end template I make a copy via template.Clone of the full template tree, hoping it in a clean state, then add the desired end template to the cloned template tree and execute it. It almost works well, except that when a {{block "FOO" .}}{{end}} is defined in the base template, but the corresponding named block is not {{define}}-ed in the end executing template, the execution result may contain the content of block FOO defined in other templates that should not be executed.
The clone and execution process is as follows:
package templates
import (
"embed"
"fmt"
"html/template"
"io"
"github.com/labstack/echo/v4"
)
//go:embed views/*.html
var templateFS embed.FS
type Template struct {
templates *template.Template
}
func NewTemplate() *Template {
templates := template.Must(template.New("").Funcs(funcMap()).ParseFS(templateFS, "views/*.html"))
return &Template{
templates: templates,
}
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
tmpl := template.Must(t.templates.Clone())
tmpl = template.Must(tmpl.ParseFS(templateFS, fmt.Sprintf("views/%s", name)))
return tmpl.ExecuteTemplate(w, name, data)
}
func funcMap() template.FuncMap {
return template.FuncMap{
"dateTime": dateTime,
"noEscape": noEscape,
}
}And the base layout template is as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width" />
<title>{{block "title" .}}{{end}}</title>
<link rel="stylesheet" href="/static/bin/style.css">
{{block "feed" .}}{{end}}
</head>
<body>
<div class="container">
{{block "content" .}}{{end}}
</div>
</body>
</html>When the blog.html template that inherits layout.html and defines block feed is executed, it works fine(that produces feed block contents):
{{template "layout.html" .}}
{{define "title"}}{{.Blog.Title}}{{end}}
{{define "feed"}}
<link rel="alternate" type="application/atom+xml" title="Blog Feed" href="{{.Feed}}">
{{end}}
{{ define "content" }}
<article>
{{with .Blog}}
<div class="content">
<div class="header">
<h2>{{.Title}}</h2>
<span>{{.Date | dateTime}}</span>
</div>
{{.Content | noEscape}}
</div>
{{end}}
</article>
{{end}}But when the index.html template that also inherits layout.html but does not define block feed is executed, the execution result also contains the content of block feed, which seems like come from blog.html.
{{template "layout.html" .}}
{{define "title"}}Home{{end}}
{{ define "content" }}
<article>
<div class="content">
<h1 class="header">Home</h1>
<p>
<a href="/blog/">Go to Blog →</a>
</p>
</div>
</article>
{{end}}The execution result:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width" />
<title>Home</title>
<link rel="stylesheet" href="/static/bin/style.css">
<!-- this line should not be produced here -->
<link rel="alternate" type="application/atom+xml" title="Blog Feed" href="">
</head>In short, in the static blog project, I want blogs.html and blog.html to have the feed link produced, but not having it on the index.html page.
The full project code can be checked here: https://github.com/kidlj/blog
What did you expect to see?
Template tree produced by template.Clone is in a clean state that doesn't generate contents from blocks that have not been defined.
What did you see instead?
Template tree produced by template.Clone generates block contents that are defined elsewhere not being executed.