nested or sub template by variable

382 views
Skip to first unread message

nz

unread,
Apr 17, 2012, 8:59:30 AM4/17/12
to golan...@googlegroups.com
Hi

I don't believe this is possible, but is there a way to pass in a variable to define which nested template to use ? For example in the following I want to tell T3 to include a template based on the value passed into the T3 template. This (of course) does not work (the parser only allows a string to follow the word template ), but is there a another way to dynamically specify the included template ? I know as an alternative I could redefine the template T3. But I am hoping for a better way ..
`{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template .}}{{end}}
{{template "T3"}}`
tmpl.Execute(os.Stdout, "T2")

thanks
Nz

Rob 'Commander' Pike

unread,
Apr 17, 2012, 9:52:03 AM4/17/12
to nz, golan...@googlegroups.com
You can't, for safety reasons in html/template. The text/template
package has the same property so the packages are consistent.

The examples in the documentation for text/template show how the same
effect can be achieved safely.

-rob

Not Zippy

unread,
Apr 17, 2012, 11:18:45 AM4/17/12
to Rob 'Commander' Pike, golan...@googlegroups.com
I thought as much, I am guessing you are referring to the ability for the template to call a method or function, which could return a generated template. I think this approach may work best regardless so I can control the trusted/untrusted content injection explicitly.

thanks
Nz

dun...@gmail.com

unread,
Sep 11, 2019, 8:40:22 AM9/11/19
to golang-nuts
What's unsafe about the code above?

The basic issue I'm trying to solve is that I want to have a general "layout" framework for web pages, in which I have a navbar at the top, a navbar at the side, and content in the mail part.  The natural way to do this would be to have a template that looks like this:

{{define "page"}}
{{template "navbarTop" .NavbarTopArgs}}
{{template "navbarLeft" .NavbarLeftArgs}}
{{template .MainContent .MainContentArgs}}
{{end}}

Then you define templates for the different main pages, and call the "page" template at the top saying which of the templates to render.

Looking around, there are basically three alternate solutions to this problem, none of which are very satisfying:

* Solution 1: Multiple parsed templates

In this one, you define your main template like this:

{{define "page"}}
{{template "navbarTop" .NavbarTopArgs}}
{{template "navbarLeft" .NavbarLeftArgs}}
{{template "content" .MainContentArgs}}
{{end}}

Then for each "main content" page, you define a separate file that contains a "content" template.

But this means having a fully separate template variable, and all the template code, for each different view of the webpage.  This seems really repetitive an inefficient.

Examples suggesting this:



* Solution 2: Invert the nesting

In this option, you have each "main content" template manually include the navbars, thus:

{{define "MainContentAbout"}}
{{template "navbarTop" .NavbarTopArgs}}
{{template "navbarLeft" .NavbarLeftArgs}}
... content of 'about' page...
{{end}}

This again is repetitive -- each page has to include the navbars, and if I (say) wanted to add a footer, I'd have to manually go and add it to each of my content pages; and I might make a mistake and miss some.

Examples suggesting this:


The problem with both #1 and #2 is that they're violating the Don't Repeat Yourself principle.

* Solution 3: Work around the limitation with functions

In this case, you define a function that will do the work of programmatically nesting templates for you, like this:

{{define "page"}}
{{template "navbarTop" .NavbarTopArgs}}
{{template "navbarLeft" .NavbarLeftArgs}}
{{content .MainContentTemplate .MainContentArgs}}
{{end}}

And then implementing a function that looks like this:

    funcs := template.FuncMap{
        "content": func(t string, arg interface{}) (template.HTML, error) {
            buf := bytes.NewBuffer(nil)
            mtmpl, err := template.ParseFiles("templates/study.html.tmpl")
            if err == nil {
                err = mtmpl.ExecuteTemplate(w, t, arg)
            }
            return template.HTML(buf.String()), err
        },
    }

This has the benefit of being able to make our layout fully parameterisable.  But it means having this weird structure where we go into a template, go out into go, and go back into a *different* template.

Examples of people recommending this approach:


It seems like it would be much better to simply incorporate #3 into the language itself.

Space A.

unread,
Sep 12, 2019, 6:35:38 AM9/12/19
to golang-nuts
Just use {{if}}...{{elseif}} statements that will check .MainContent equality to specific value and will invoke specific {{template ...}}
Reply all
Reply to author
Forward
0 new messages