-
-
Notifications
You must be signed in to change notification settings - Fork 4k
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
templates: Add template function "import" to enable {{ template ... }} and {{ block ...}} #4321
Conversation
Add {{ render "/path/to/file.ext" $data }} via funcRender
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR, this seems like a handy change.
Do you think we can do it with less code repetition? Might require some slight refactoring.
Definitely. Here is what I did: Call include from render with a unique Arg to trigger the Execute to send func (c TemplateContext) funcRender(filename string, data interface{}) (returns) {
render := "cmVuZGVy" // Unique string (render base64 encoded, actually)
return c.funcInclude(filename, data, render)
} And then modify executeTemplateInBuffer: if len(c.Args) > 1 {
if c.Args[1] == "cmVuZGVy" {
return parsedTpl.Execute(buf, c.Args[0])
}
}
return parsedTpl.Execute(buf,c)
} Another option... I already did the first way because I think it's better, but if you don't like it I can do this second way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is definitely less repetition!
But the secret argument with a special value is not my favorite solution.
Can we instead refactor funcInclude
slightly so that it has an "external" API (the one registered in the FuncMap for use in templates) and an "internal" API? Even if the only difference is a single parameter that specifies what value to use as the template context.
Hmm...I can't think of a way to do it by only refactoring But, I could add an argument to func (c TemplateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer, mode int) error {
...
// mode = 0, use c
// mode = 1, use c.Arg[0]
if mode == 1 {
return parsedTpl.Execute(buf, c.Arg[0])
}
return parsedTpl.Execute(buf, c) I'd have to change the two other calls to Thoughts? |
Thanks for working on this, it might be a while before I get around to this again since I have a conference coming up. Anyone else is welcome to give a review/feedback though in the meantime. |
I rethought the problem and came up with a better solution.
Example of how it works: import.html
index.html
You can also pass data into template, which will be used as You can also use {{ block ...}} from the standard library too. Good luck with the conference, @mholt! |
Last commit is fixing a bug I found. I made funcImport return a nil error, but that was showing up in the html as |
Cool, looking forward to taking a look at this once I get caught up with some things! |
@rockorager Do you know if something similar could be done to solve #2094? Do you think this is close enough? |
This does solve the overwrite issue mentioned (as long as your imports are done above the block/template spots). For example: index.html
post.md
Will render as
In order to do this, index.html has to be the template that is first called (it gets parsed, then executed. During execution the import function re-calls parse - before the index.html
It doesn't solve the That approach breaks if you have multiple layers: Template 1 extends Template 2, which imports Template 3 which extends Template 4. I don't think the solution above would handle I think that could be fixed by also treating import in the pre-process way and using the internal parse nodes to build the template tree and find the base. I built a quick example using the internal parse package to find the extends. The result of parse.Parse can be manually added to a ParseTree of a template...means we only have to parse once. Anyways. It's probably close enough that you can't import a template that extends another template. I'll try working on the extends issue for another PR. |
Ok, gotcha. That sounds good. What was the reason for changing |
Agreed it's close to
vs
|
Ah, gotcha. Technically though, That's fine, but we just need to be sure to document the nuances here. Would you be able to update the godoc comments where the template actions are all documented so that it describes this change and the nuances? Thanks! |
That's right, and then to call where to render any doc you would reference it by it's path in block or template. post.html
index.html
Yep, I'll update the docs and push a new commit! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks so much for iterating on this. Let's give it a try.
Related to (closed) Issue #2094 on template inheritance. This PR adds a new function called "render" which works like "include", except it only takes one argument and passes it to the referenced file to be used as "." in that file.
Basic usage:
base.html
/path/layout.html
Output
Possible use-case usage:
Use-case for me was to more cleanly pass markdown data to templates. Using include required having something like
{{ $data := index .Args 0 }}
at the beginning of any child template.markdown.md
base.html
/path/layout.html
Output