5

I would like to write such a conditional fragment in a Go HTML template :

  {{if isUserAdmin}}
     <a href"/admin/nuke">Go to the big red nuclear button</a>
  {{end}}

However, this is not directly possible because the template is not aware of the request that triggered its execution, so it cannot determine if the user is admin or not.

Is there some normal way to achieve this ?

In advance I point out that :

  • I do not want to use Pipelines for this specific data (see other question about this)
  • I acknowledge that only the handlers/controllers should deal with logic, and views should only do the rendering. But the condition {{if isUserAdmin}} is not logic itself, it's a necessary construct to leverage a boolean value already calculated by the controller.
  • The Funcs method can help, but is currently not lean enough for easily defining specific method isUserAdmin()
Community
  • 1
  • 1
Deleplace
  • 6,812
  • 5
  • 28
  • 41

3 Answers3

4

I would agree with Darshan Computing, I think the proper way of passing information from the request would be in the pipeline. You can have your data being split between the data you want to render and the context, e.g. by having your template data structure embed them both if you want to clearly separate the two:

type TemplateData struct {
    *Content
    *Context
}

Which gives this for example. You can then reuse some of your context/content information depending on what is shared and what is query specific.

val
  • 8,459
  • 30
  • 34
  • Yes I am currently using this approach in my project. It is really cumbersome when dealing with many subtemplates : either my root Pipeline is a hierarchy containing structures with **a lot** of redundancy (each data passed to a subtemplate must have its own pointer to the context), or the context must be dynamically passed by multiplexing sub-pipelines as discussed in other question. – Deleplace Aug 19 '13 at 09:17
  • Hmm, the best I can come up with is to have the 'passing context' done as simply as possible by embedding it in ALL data: http://play.golang.org/p/b-uXPuljyl. But this leads to very awkward `.Content` prefixes all over the place. As template seem to be perfectly insulated from outside world (probably to protect from races, as you mentioned), I am not sure you can really simplify it a lot beyond that... – val Aug 19 '13 at 10:40
3

Here is a working solution attempt (link to Playground) using Funcs to overwrite "isAdmin", after template compilation but before each execution (thanks to Valentin CLEMENT in other question).

But it has several flaws :

  • It is weird to declare a dummy empty "isAdmin" function before template compilation.
  • (Using Funcs several times is painful because I cannot just overwrite a single method, I have to provide a complete FuncMap with all the functions) edit : in fact previous funcs are not lost, i was wrong about that.
  • It is inherently not thread-safe and will fail when several goroutines alter and execute the same template
Community
  • 1
  • 1
Deleplace
  • 6,812
  • 5
  • 28
  • 41
3

The normal thing to do is to simply pass your template a struct with whatever static data you like. Unless I've misunderstood what you're trying to do, there doesn't seem to be any need for Funcs here. Simplifying your example:

package main

import (
    "html/template"
    "os"
)

const hometmpl = `
{{if .IsAdmin}}
  <a href="/admin/nuke">Go to the big red nuclear button</a>
{{end}}
`

var t = template.Must(template.New("home").Parse(hometmpl))

func isAdmin(token string) bool {
    const adminToken = "0xCAFEBABE"
    return token == adminToken
}

func main() {
    token := "0xCAFEBABE" // Or extracted from the http.Request
    t.ExecuteTemplate(os.Stdout, "home", struct{IsAdmin bool}{isAdmin(token)})
}
Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
  • I am trying to avoid putting the bool in the Pipeline because as you said elsewhere a lot of things get quickly "a little cumbersome" to code. Imho the main Pipeline passed to the template should be a full structure containing the actual data we want to render, but other typical Context data should be available without having to multiplex it in every pipeline of every subtemplate : current language and locale, user profile, ACL. – Deleplace Aug 19 '13 at 08:24