3

My setup:

base.tmpl

{{define "base"}} ...basic hmtl boilerplate ...{{end}}
{{define "post"}}
    {{.ID}} {{.Username}} etc... 
    {{if $.User.Admin}}
        <...admin controls...>
    {{end}}
{{end}}

index.tmpl

{{template "base" . }}
{{define "content"}}
    Stuff......
    {{range .Posts }}
        {{template "post" . }}
    {{end}}
{{end}}

But I get

$.User.Admin is not a field of db.Post

How can I get the values from the "global" dot value within a template that isn't given it? $. clearly doesn't work.

I was just having the post within the range, but I added a new page to view individual posts and would like to not update each area that posts are displayed individually.

Update: Templates are executed like so

func Index(rw http.ResponseWriter, req *http.Request) {
    g := GetGlobal(rw, req) // Gets logged in user info, adds branch/version info etc
    ... Get posts from the DB ...
    if err := Templates["index.tmpl"].Execute(rw, g); err != nil {
        Log.Println("Error executing template", err.Error())
    }
}

The Global struct looks like:

type Global struct {
    User     db.User
    Users    []db.User
    EditUser db.User
    Session  *sessions.Session
    Error    error
    Posts    []db.Post
    Post     db.Post

    DoRecaptcha bool
    Host        string
    Version     string
    Branch      string
}

Templates are loaded like so:

Templates[filename] = template.Must(template.ParseFiles("templates/"+filename, "templates/base.tmpl"))
THUNDERGROOVE
  • 197
  • 1
  • 10
  • Might consider adding some other tags. Your question is specific to the templating solution you're using. I know Go well but know nothing about this subject. – evanmcdonnal Apr 13 '15 at 21:23
  • 1
    I'm using the built-in html/template package. I couldn't exactly find an established tag related to it so I didn't bother e: Found one – THUNDERGROOVE Apr 13 '15 at 21:30
  • Yeah, I found the package easily enough. Thanks for editing, it will be helpful in the future. Go is lacking in SO adoption but also the questions are pretty much all in the general 'go' bucket where as in say .NET there are tags for all the common libraries. – evanmcdonnal Apr 13 '15 at 21:34
  • Yeah that's much in part with how much Golang manages to pack into their standard libraries. If I can't find anything I may just have to add a *db.User field into my posts and just fill it before rendering the page. Not very elegant but it would work. – THUNDERGROOVE Apr 13 '15 at 21:36
  • How are you passing the values to the template? Via a map? Can you post that code too? e.g. `template.Execute(...)` – elithrar Apr 13 '15 at 22:18
  • For now, I'm just adding the db.User pointer to each post before rendering the template. It's not pretty but it works for now. If anyone can still figure out the question I would love it. – THUNDERGROOVE Apr 13 '15 at 22:43

1 Answers1

4

Invoking a template creates a new scope:

A template invocation does not inherit variables from the point of its invocation.

So you have three options:

  1. Add a reference to the parent in the post (as you said you're doing in your last comment)
  2. Create a function (isAdmin) that references the variable through closure:

    template.New("test").Funcs(map[string]interface{}{
        "isAdmin": func() bool {
            return true // you can get it here
        },
    }).Parse(src)
    

    The downside of this approach is you have to parse the template every time.

  3. Create a new function which can construct a post that has a reference to the parent without having to modify the original data. For example:

    template.New("test").Funcs(map[string]interface{}{
        "pair": func(x, y interface{}) interface{} {
            return struct { First, Second interface{} } { x, y }
        },
    }).Parse(src)
    

    And you could use it like this:

    {{range .Posts }}
        {{template "post" pair $ . }}
    {{end}}
    

    Another option is to make a dict function: Calling a template with several pipeline parameters

Community
  • 1
  • 1
Caleb
  • 9,272
  • 38
  • 30