-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
text/template: {{block}} in text/template #3812
Comments
Blocks could be inlined during pre-process: that is, you call a template and its parse tree is inlined in the current template, with the blocks replaced. With this approach, blocks are just sugar to merge templates and aren't included in the final parse tree (at the cost of more parsing time && larger parse trees). The pre-processed template is secured normally, as if it was a normal template. That said, the mechanism for blocks is still lacking. |
I rethought the {{block}} mechanics and have an proof-of-concept implementation. It is explained in this code review: http://golang.org/cl/6447044/ Should I send it to the mailing list for further discussion? |
Labels changed: added go1.2maybe. Owner changed to @robpike. |
I now believe this is easy to support in html/template and would like to add this feature to Go 1.2 but have questions. Why did you add a "fill" action in your CL? I don't see why it's necessary because the execution stack could hold the scoped blocks. I haven't looked closely at your CL yet because I want to understand the design before the implementation. |
The design changed a couple of times since I submitted this. At the time the idea was to differentiate a block declaration from its usage. For familiarity and consistency with existing systems that support inheritance (a.k.a. Django and all the engines that followed the idea), probably a single action should be used. I'd invite the community for some brainstorm about the specific semantics, maybe. :-) |
When trying to write up a proposal to implement this, I realized I still do not understand it. In your example we have {{{ {{define "Page"}} {{block body}} <p>Another body</p> {{end}} {{template "Base" .}} {{end}} }}} When we invoke {{template"Page"}}, how do we know not to display the text "Another body" before we reach the instantiation of the "Base" template? |
When I implemented this (or one of the variants), the template set was locked after all templates were parsed, and blocks were "expanded" when appropriate. By expanded I mean: block nodes were replaced in the resulting tree, when they were overridden. So execution needed no changes, because the block nodes were not part of the resulting tree. This is not possible in current text/template, because it doesn't lock the tree after parsing (like html/template does -- a requirement of the escaping mechanism). (Not-Go-1.x parenthesis: A discovery I made during this experiment is that text/template and html/template could share a lot more code -- specially execution -- if both locked the tree after parsing. They could, or should imo, be a single package with the appropriate API to request a template instance for HTML context or not. Escaping is just an after-parsing tree expansion, like blocks or new actions or semantics could be.) I think the particular proposal posted in this issue, as it is, must be forgotten. And if an inversion-of-control functionality to assemble templates must be added, the specific semantics should be re-thought from scratch. I'm sorry for the lack of responses, but I've had no time to come back and think about this in deep. If you wish, please consider the issue abandoned. |
As an example of a different proposal, see zap, which uses a inheritance syntax and mechanics similar to Django/Jinja2. but zap is a parser that produces text/template-compatible templates expanding `{{block}}`tags: https://code.google.com/p/sadbox/source/browse/zap/doc.go It adds a `{{block}}` action and a second optional parameter to `{{define}}`, to define the template that is being overridden. |
Has there been more discussion on this elsewhere? Where would be a good place to kick this off again? |
Rob and I just designed a potential solution to this issue. It introduces a new
See this issue for the details: https://go-review.googlesource.com/#/c/14005 Still TBC are the helpers (OverlayFiles, OverlayGlob, etc) and html/template support. |
CL https://golang.org/cl/14005 mentions this issue. |
To clarify the mechanics of For example, this:
after parsing, is effectively this:
|
This takes care of what I've used inheritance for in Django (and, before that, Perl's HTML::Mason 1.x): we have a master with the HTML boilerplate, the code to load common scripts, etc., and the content of site-wide headers/footers; other ("overlay") templates provide a chunk of body content, may add extra resources they need to pull in, may vary title, meta tags, legal fine print in the middle of the footer, or other stuff. I like that in the proposal overlays don't specify their master, unlike in Django and Mason. Sometimes one set of overlays (forms) need to be inserted into more than one master (brand/"skin"), and Django and Mason make that ugly to do. One misfeature of Django template inheritance is that you it'll let you put content outside any block in an overlay template and it silently discards it. It looks like this CL does that, too. Probably not worth the mess, but it'd be cool if |
I'm working on a localization design (doc going to golang-dev soon) which includes a proposed extension to templates akin to Soy. The most notable feature is a msg keyword with functionality not dissimilar to this, but different enough to not allow this as a solution. I can simulate a "msg" keyword by using {{msg}}/{{msgend}} pairs, instead of {{msg}}/{{end}}, but the latter form is clearly ideal. Anyway, the point is that the solution is fairly similar: parse the template using template/parse, rewrite the template to something understandable by the core template package, Execute. So it may be an idea to instead of defining the new overlay construct in core, extend template/parse to allow user-definable actions and rewrite capabilities, similar to allowing users to specify Funcs. The core library could still define an off-the-shelf rewriter like the one proposed. Although I'm a little bit weary about adding features to a core package for something that is just syntactic sugar. The rewriter for msg could reside in the text repo and would not need to be in core, etc. |
I'm confused. I replied on the CL. |
@rsc - Some template systems do arrange so that you don't need to write any non-template code to glue two templates together. In Django you put The The extra lines to invoke |
Not in response to anything in particular, but just since it hasn't come up in discussion much, noting that master templates often have a few blocks. (Just looked at one with seven, for example.) Imagine that one part of the site needs to load additional scripts in the header, another needs a different organization's logo/name/nav links atop the page and tweaks to the legal boilerplate in the footer, a third wants to generate its title/description in its own way, etc. You make blocks to help do each of those things. That's part of why users want blocks and not just, say, a header template and a footer template. Maybe an example with two blocks is worth it, but maybe users don't need the handholding. |
I replied to @rsc on the CL. |
(Reposting from my tweet to Andrew). For me, a quintessential example for HTML would be setting a default const (
layout = `<title>{{block "metaTitle" .}}Welcome to golang.org{{end}}</title><body>{{block "content"}}{{end}}</body>`
aboutPage = `{{define "metaTitle"}}Read all about Go on golang.org{{end}} {{define "content"}}About page content{{end}}`
)
layoutTmpl, err := template.New("layout").Parse(layout)
if err != nil {
log.Fatal(err)
}
aboutPageTmpl, err := layoutTmpl.Overlay(aboutPage)
if err != nil {
log.Fatal(err)
}
if err := layoutTmpl.Execute(os.Stdout, nil); err != nil {
log.Fatal(err)
}
if err := aboutPageTmpl.Execute(os.Stdout, nil); err != nil {
log.Fatal(err)
}
// Output:
// <title>Welcome to golang.org</title><body></body>
// <title>Read all about Go on golang.org</title><body>About page content</body> I hope this is useful feedback. Would the child/overlay/aboutPage be able to get the default block content from the master with something like E.g. const (
layout = `<title>{{block "metaTitle" .}}golang.org{{end}}</title>`
aboutPage = `{{define "metaTitle"}}Read all about Go - {{parent}}{{end}}`
)
// Output:
// <title>golang.org</title>
// <title>Read all about Go - golang.org</title> |
I may be late to the party, but let me propose an alternative design:
This enables the following: master:
page:
Here the "page" explicitly passes the name of the template to be used as a content block. The "master" checks if content block was passed, and if it is, uses it. Otherwise, master uses the default content. This design relies on the fact that template scope is global and Here is a proof of concept, with defined |
On the other hand, this might be too verbose, and with a dynamic template name, html/tempalte would not be able to escape correctly. |
@nodirt The reason the template action does not accept a dynamic expression for the name of the template to invoke is that that makes it infeasible to provide the safety that the html/template package guarantees. In fact, the ability was deleted from text/template when html/template was added. |
I've just uploaded a revised version of the change that removes the https://go-review.googlesource.com/#/c/14005/2 Redefinition is always permitted in |
This is no longer required as [Go #3812](golang/go#3812) should have resolved this problem.
The text was updated successfully, but these errors were encountered: