In a Go template, sometimes the way to pass the right data to the right template feels awkward to me. Calling a template with a pipeline parameter looks like calling a function with only one parameter.
Let's say I have a site for Gophers about Gophers. It has a home page main template, and a utility template to print a list of Gophers.
http://play.golang.org/p/Jivy_WPh16
Output :
*The great GopherBook* (logged in as Dewey)
[Most popular]
>> Huey
>> Dewey
>> Louie
[Most active]
>> Huey
>> Louie
[Most recent]
>> Louie
Now I want to add a bit of context in the subtemplate : format the name "Dewey" differently inside the list because it's the name of the currently logged user. But I can't pass the name directly because there is only one possible "dot" argument pipeline! What can I do?
- Obviously I can copy-paste the subtemplate code into the main template (I don't want to because it drops all the interest of having a subtemplate).
- Or I can juggle with some kind of global variables with accessors (I don't want to either).
- Or I can create a new specific struct type for each template parameter list (not great).
Sometimes maps are a quick and easy solution to situations like this, as mentioned in a couple of the other answers. Since you're using Gophers a lot (and since, based on your other question, you care if the current Gopher is an admin), I think it deserves its own struct:
Here's an update to your Playground code: http://play.golang.org/p/NAyZMn9Pep
Obviously it gets a little cumbersome hand-coding the example structs with an extra level of depth, but since in practice they'll be machine-generated, it's straightforward to mark
IsCurrent
andIsAdmin
as needed.You could register a "dict" function in your templates that you can use to pass multiple values to a template call. The call itself would then look like that:
The code for the little "dict" helper, including registering it as a template func is here:
based on @tux21b
I have improved the function so it can be used even without specifying the indexes ( just to keep the way go attaches variables to the template)
So now you can do it like this:
or
or
but if the parameter (.CurrentUser.name) is not an array you definitely need to put an index in order to pass this value to the template
see my code:
The way I approach this is to decorate the general pipeline:
by creating a context-specific pipeline:
Allocating the context-specific pipeline is very cheap. You get access to the potentially large
HomeData
by copying the pointer to it:Since
HomeData
is embedded inHomeDataContext
, your template will access it directly (e.g. you can still do.Popular
and not.HomeData.Popular
). Plus you now have access to a free-form field (.I
).Finally, I make a
Using
function forHomeDataContext
like so.This allows me to keep a state (
HomeData
) but pass an arbitrary value to the sub-template.See http://play.golang.org/p/8tJz2qYHbZ.
Ad "... looks like calling a function with only one parameter.":
In a sense, every function takes one paramater - a multivalued invocation record. With templates it's the same, that "invocation" record can be a primitive value, or a multivalued {map,struct,array,slice}. The template can select which {key,field,index} it'll use from the "single" pipeline parameter in whatever place.
IOW, one is enough in this case.
The most straightforward method (albeit not the most elegant) - especially for someone relatively new to go - is to use anon structs "on the fly". This was documented/suggested as far back as Andrew Gerrand's excellent 2012 presentation "10 things you probably don't know about go"
https://talks.golang.org/2012/10things.slide#1
Trivial example below :
Note that this won't technically run as-is, since the template needs some minor cleaning-up (namely getting rid of the comma on the last line of the range loop), but that's fairly trivial. Wrapping the params for your template in an anonymous struct may seem tedious and verbose, but it has the added benefit of explicitly stating exactly what will be used once the template executes. Definitely less tedious than having to define a named struct for every new template you write.