Why is it that Go templates do not automatically parse embedded templates?

719 views
Skip to first unread message

Meester Guy Person

unread,
Aug 11, 2014, 12:34:52 PM8/11/14
to golan...@googlegroups.com
Something that I have wondered since I began using Go is why embedded templates are not parse automatically.  It seems like it would be simple enough to set up a path for all the various places we want to keep our templates and then automatically treat the names therein as paths to files within one of those directories.  Seems very tedious to have to to list out the files that need parsing, despite their being embedded and obviously necessary to the realization of the surrounding template.  I understand some convention would need to be introduced to distinguish a named template from a template path, but that seems doable.  So is this something that is being investigated?  Any reason not to implement it?

On a related note, I'm curious about how efficient Go templates are.  Are they parsed every time, or is some optimized form of the parsed template cached somewhere?  How expensive is it to parse a template?  If it is relatively inexpensive, would it be good practice to simply parse everything using ParseGlob, rather than listing our all templates to make the code more maintainable?

Matt Silverlock

unread,
Aug 11, 2014, 9:01:09 PM8/11/14
to golan...@googlegroups.com
I would say that the current, explicit behaviour is about avoiding any "magic" that makes where templates are coming from unclear. There's also the fact that you don't always want to put templates into one giant glob - it's too implicit.

If you want to automatically build a template map instead of writing out files, there's nothing stopping you from creating a map[string]*template.Template that you populate with a range clause or two. I wrote about some other html/template tricks a month ago, and in the second last example you can see how to do this: http://elithrar.github.io/article/approximating-html-template-inheritance/

    if templates == nil {
        templates = make(map[string]*template.Template)
    }

    layouts, err := filepath.Glob(config.Templates.Path + "layouts/*.tmpl")
if err != nil { log.Fatal(err) } includes, err := filepath.Glob(config.Templates.Path + "includes/*.tmpl")
if err != nil { log.Fatal(err) } // Generate our templates map from our layouts/ and includes/ directories for _, layout := range layouts { files := append(includes, layout) templates[filepath.Base(layout)] = template.Must(template.ParseFiles(files...)) }


Go's templates are parsed into an AST but rendered on demand. Rendering is relatively quick, but if you're passing large objects (i.e. slices of structs) expect a performance hit over serving "Hello World". They're still much faster any of the dynamic language template libs out there though, and there's no disk reads if you set things up correctly.

Meester Guy Person

unread,
Aug 12, 2014, 8:47:39 AM8/12/14
to golan...@googlegroups.com
In general, I like the notion of "explicit" in programming.  Less time spent pouring over documentation, more time gaining experience with the code.  At the same time, a convenience function here or there might be nice.  Guess that's sort of what I would like to see here -- a single function that would parse some base template file and then recursively continue to parse any child template files (say names beginning with a "/" to indicate the root template directory from which to reference a file, using a predeclared set of template path directories).  And of course the files for parsing are explicitly listed in the template.  My issue with the current design is that I have to explicitly list files twice, once by path in ParseFiles, and once by name in the templates.  This is confusing behavior, in my opinion.

Believe I found and bookmarked your solution a while back for study.  I think it's a helpful idea, but, assuming I'm reading it correctly, it requires that you stick to a particular structure -- all templates have to go in "layouts" and "includes" directories here, whereas more complex multi-level hierarchies would not be supported.  But the hierarchies are so important for organization, especially in larger projects.

I guess if someone could show me one instance where "Base.html" includes a template "Child.html" that did not need to be parsed in order to realize Base.html, that might help make sense of the current system.  But if Child.html will always need to be parsed in order for Base.html to be realized, why not make such parsing implicit and inherit any delims / funcs from the parent template into the child?

Thanks for the info on the storage format for templates.  I will have to look into that.

Matt Silverlock

unread,
Aug 12, 2014, 9:28:23 AM8/12/14
to golan...@googlegroups.com
> it requires that you stick to a particular structure -- all templates have to go in "layouts" and "includes" directories here, whereas more complex multi-level hierarchies would not be supported.  But the hierarchies are so important for organization, especially in larger projects.

Not at all. You can use filepath.Walk to navigate down and do whatever you'd like—but in a blog post it pays to keep to the common/simple case. There are packages that can handle this for you and walk the tree — I believe https://github.com/unrolled/render can do it.

> I guess if someone could show me one instance where "Base.html" includes a template "Child.html" that did not need to be parsed in order to realize Base.html, that might help make sense of the current system.  But if Child.html will always need to be parsed in order for Base.html to be realized, why not make such parsing implicit and inherit any delims / funcs from the parent template into the child?

Can you clarify this? Templates need to be parsed together (that is, any children need to be parsed with the parent) — if you have a parent template that requires a {{ template "head" }} {{ template "sidebar" }} and {{ template "footer" }} then you need to parse child templates that populate those. If your "base.html" doesn't have any such fragments (i.e. it can be rendered in full, by itself) then you don't need to do anything special.

James Bardin

unread,
Aug 12, 2014, 9:34:59 AM8/12/14
to golan...@googlegroups.com
I think he may be referring to the package function template.ParseFiles, which if you give it parent.html and child.html, calling Execute doesn't contain the child.html. 

template.ParseFiles docs say "The returned template's name will have the (base) name and (parsed) contents of the first file", which can be confusing, since template.Template.ParseFiles does embed the child template. 

Meester Guy Person

unread,
Aug 12, 2014, 11:17:28 AM8/12/14
to golan...@googlegroups.com
To clarify, I am thinking along the lines of a ParseFiles that only requires the base template to be listed.  So in creating the template, I would do something like this:

t, err := template.ParseBaseFile("base.html")

And base.html would look something like this:

<!DOCTYPE html>
<html>
 
<head></head>
 
<body>Child is {{ template "/child.html" . }</body>
</html>

So it's clear hear that an additional template needs to be parsed to render the base template.  If we just take "/child.html" as a name, then we need to have this defined somewhere.  What I am suggesting instead is that when we run across something with a leading slash (or some other means of denoting this as a file path instead of a name), we treat this as a file path that is relative to a root template path directory specified previously, in which case we have all the information we need there in the template to locate the file and attempt to parse it.  In this manner, we could parse all embedded templates in the process of parsing their containing templates.  This cannot be done if "/child.html" is treated strictly as a name, of course.  As a file path, however, it is self-defining.

So if we were able to specify file paths instead of just names within templates, not only would this be more explicit -- I know exactly where to look for the template that will be included here, rather than having to go back and match the name to wherever its content is stored -- but this would eliminate the possibility of conflicts created by files having different paths but the same base name as might happen when using ParseFiles and it would be good deal more straightforward and in turn maintainable.  My opinion.

As a bonus, if there were a way of setting a path list for templates pointing to embedded file templates, then packages could add to or modify this list, so that default templates could easily be provided and overridden as desired, since each path could be checked in turn for the specified file, first one found wins.

Does that help to understand what I'm getting at?



On Tuesday, August 12, 2014 9:28:23 AM UTC-4, Matt Silverlock wrote:
Reply all
Reply to author
Forward
0 new messages