How do you pass multiple objects to go template? - templates

Most examples I can find describe very simple/basic things, such as showing attributes of a person object like this:
The name is {{.Name}}. The age is {{.Age}}.
What happens if you have a more complicated web page, for example, multiple different objects and lists of objects, i.e. How do you do something like this:
{{p.Name}} is aged {{p.Age}}.
Outstanding invoices {{invoices.Count}}
<table>
<tr><td>{{invoices[0].number}}</td></tr>
.... etc...

You can declare and pass in an anonymous struct like this:
templ.Execute(file, struct {
Age int
Name string
}{42, "Dolphin"})
and access the variables like:
{{.Age}}, {{.Name}}
While this still requires you to make a struct, it is among the most concise ways to do it. You'll have to decide if it is too ugly for you ;)

You can put your more complex data into struct, and pass it just like you did Name and Age. For example,
type vars struct {
P User
Invoices []Invoice
}
type User struct {
Name string
Age int
}
type Invoice {
Number int
Description string
}
If you pass an instance of vars into the template execution, you can reference sub-structures by using dots and array indexes, just like in regular go code.
{{.P.Name}}, {{.P.Age}}, {{.Invoices[0].Number}}

It depends on what your data is.
I want to categorise this.
Primary data that the template is meant for. In your example that would be Invoice/Invoicelist. If you have to pass more than one of these you have to reconsider your template design.
Secondary data such as logged in user information or any common information that you find yourself passing into several templates.
Since these information are common. I usually make them into functions. Since these functions cannot have input params. You might want to create them as closures (within another function). Assign these function to funMap and add it to the template after parsing.
func MakeFuncMap(u *user) map[string]interface{} {
return map[string]interface{}{
"User": func() *user {return u}, //Can be accessed by "User." within your template
}
}
t, err := template.New("tmpl").Funcs(MakeFuncMap(nil)).Parse("template") //You will need to append a dummy funcMap as you will not have access to User at the time of template parsing
//You will have to clone the template to make it thread safe to append funcMap.
tClone, _ := t.Clone()
tClone.Funcs(MakeFuncMap(u)).Execute(w, invoicelist)
Now you can execute the template with only the invoicelist as data.
Within your template you should be able to access user information using "User." and invoice list by "."
You should be able to define the funcMap once for all the common data. So that you will be able reuse it.
To loop through a invoicelist you can look into range
{{range .}} //if you are passing invoicelist then the . means invoicelist
//in here . means each of the invoice
<label>{{User.Name}}, {{User.Age}}</label>
<label>{{.Id}}</label>
{{end}}
EDIT: Included fix for issue pointed out by Ripounet

Related

Pug/Jade get all variables in a given template

For a given Jade/Pug template I would like to get a list of all variables which occur within the template.
My motivation is as follows: In my software, different templates are used to generate some HTML snippets. Based on a given context (i.e. values for certain variables are given), I would like to suggest only those templates, where all variables within the template can be assigned.
Example: For template myTemplate like this:
html
head
title= myTitle
body
h1 #{value.headline}
p #{paragraph.text}
I would like to get some output like this:
var variableNames = extractVariableNamesFromTemplate('myTemplate');
// variableNames = [ 'myTitle', 'value.headline', 'paragraph.text' ]
Is there something available ready-to-use? Preferably a solution which would take into account all language-specific features such as includes, extends, etc.
This is not a full answer to your problem but more of a starting point. From debugging the pug code, i have noticed you could probably "hook" a plugin in one of the steps of template "compilation" to code. Look here.
It seems that in the various steps of compilation, you can access the diffrent nodes present in the template.
You could also look at this, it seems to offer almost what you are looking for.
If you do something like
var lex = require('pug-lexer');
var filename = 'template.pug';
var src = `
html
head
title= myTitle
body
h1 #{value.headline}
p #{paragraph.text}`;
var tokens = lex(src, {filename});
The contents of tokens is an array of the diffrent tokens, the one that are of type "code" or "interpolate-code" seem to be the diffrent variables.
Hope this helps

Accessing Sitecore template field values

This may be documented somewhere, but I cannot find it.
I am using the Sitecore helper and razor syntax to place values into my view:
#Html.Sitecore().Field("foo");
This works fine, but, I have Fields defined in Groups, and a few of them have the same name, like:
Group1: foo
Group2: foo
Question: Is there any way to access the field by group?
Something like this (what I have already tried):
#Html.Sitecore().Field("Group1.foo");
As long as I know it is not possible to use the sections name.
I would avoid to have the same field name even between differents sections.
Or a option is to pass the field ID and then create classes or constants to not hard code IDs
#Html.Sitecore().Field("{E77E229A-2A34-4F03-9A3E-A8636076CBDB}");
or
#Html.Sitecore().Field(MyTemplate.MyGroup.Foo); //where foo returns the id.
EDIT:
MyTemplate.MyGroup.Foo would be classes/structs you created your own just to make easier and more reliable the references all over you project. (you can use a tool to auto-generate it like a T4 template)
public static struct MyTemplate
{
public static struct MyGroup1
{
public static readonly ID Foo = new ID("{1111111-1111-1231-1231-12312323132}");
}
public static struct MyGroup2
{
public static readonly ID Foo = new ID("{9999999-9999-xxxx-1231-12312323132}");
}
}
Try accessing via the GUID of the field. This way, even if they have the same name, the API will be able to load the appropriate field.

tt_address: add categorys of address to the template

Is it somehow possible to add the sub-group of a cetrain group the address is assigned to the html output?
In the template I have ###MAINGROUP### and ###GROUPLIST###. I can't use maingroup, cause it's not the case that the group I need is always the maingroup. And with the grouplist I can't say which group is the sub-group of the one group.
Anyone have an idea how I could do it?
And in addition to that I also need the value of a self created field in the tt_address table.
Edit:
I try it like #lorenz say. What I have so far:
ext_localconf.php:
<?php
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['tt_address']['extraItemMarkerHook'][]
='EXT:txnextaddresssort/class.tx_next_address_sort_addmarkers.php:tx_next_address_sort_addmarkers';
class.tx_next_address_sort_addmarkers.php:
<?php
class tx_next_address_sort_addmarkers {
function extraItemMarkerProcessor(&$markerArray, &$address, &$lConf,
&$pObj) {
$lcObj = t3lib_div::makeInstance('tslib_cObj');
$lcObj->data = $address;
$markerArray['###SORTBEREICH###'] =
$lcObj->stdWrap($address['tx_nextaddresssort_sort_bereich'],
$lConf['tx_nextaddresssort_sort_bereich.']);
}
}
Extentionkey: next_address_sort
All I get is a blank screen, but no errors in apache log
No, there is no possibility to do that.
Yet you can write a custom extension that integrates the extraItemMarkerProcessorhook in tt_address. In ext_localconf.php, add:
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['tt_address']['extraItemMarkerHook'][] ='EXT:myextension/class.tx_myextension_filename.php:tx_myextension_classname';
Then add a file class.tx_myextension_filename.php to your extension.:
class tx_myextension_classname {
public function extraItemMarkerProcessor(&$markerArray, &$address, &$lConf, &$pObj) {
$lcObj = t3lib_div::makeInstance('tslib_cObj');
$lcObj->data = $address;
$markerArray['###MYFIELD###'] = $lcObj->stdWrap($address['myfieldlikeindatabase'], $lConf['myfieldlikeindatabase.']);
return $markerArray;
}
}
This would be an example for getting a field that is in the tt_address table and adding it to the markers so they can be used in a template. It is also stdWrap enabled.
Now, instead of getting a field, you should replace $address['myfieldlikeindatabase'] with a variable that contains the information you need. To receive the data, you can use the TYPO3 database API functions ($GLOBALS['TYPO3_DB']).

Leave out passthrough parameters

Consider this example:
case class Home(description: String)
case class Person(age: Int, race: String, home: Home)
def age(p: Person): Person = {
val newAge = p.age + 1
p.copy(age = newAge, home = if (newAge == 18) Home("Under the bridge") else p.home)
}
it("Should move on 18th birthday") {
val person18yrs = age(Person(17, "Caucasian", Home("With parents")))
person18yrs shouldBe Person(18, "Caucasian", Home("Under the bridge"))
}
Now if I want to test the method age, I need to fill the field race even though the method age doesn't discriminate the person object based on it's race. It's only pass-through parameter. In this trivial example, it's not so much work, but when i work with two fields in a 20-field class hierarchy, I'm not happy. And I want to be happy. So I start to look around for some solution.
One solution might be to fill the empty fields with nulls. But the downside is if I then change the implementation, it would convert test classes compile error to tests failure. And I still need to write these nulls.
The other solution might be to just create the methods so they accept and return the parameters with which they interact. The downside is that I need to return tuples, which lack the name or I need to create some classes that encapsulate the method parameters and return types.
Or maybe the smart folks of stackoverflow do have some other solution, that half-blind eye of my intelligence cannot see. :-)
Create one completely filled person as a prototype, and then just create copies with the values that are relevant to your specific test case.
val protoype = Person(16, "Caucasian", Home("With parents"))
val person18yrs = age(protoype.copy(age = 17))
You could define factories with the same names as your case classes but fewer arguments, and have them create corresponding objects to which you pass default arguments, for example:
case class Person(age: Int, race: String, home: Home)
def Person(age: Int): Person = Person(age, "", Home(""))
Access to these factories could be limited to the test suite in order to avoid that they are used in non-rest-related code of your application.

Looping over an array of objects in a template (Go)

I'm passing a struct (one element is an array of Category objects) to the template for rendering. In the template, I have code that looks something like this:
{.repeated section Categories}
<p>{#}</p>
{.end}
However, each Category has a few of its own elements that I need to be able to access (Title for instance). I have tried things like {#.Title} but I can't seem to find the proper syntax for accomplishing this. How do I access members of data in an array during a loop in a template?
You can just write {Title}.
Whenever the template package encounters an identifier, it tries to look it up in the current object and if it doesn't find anything it tries the parent (up to the root). The # is just there if you wan't to access the current object as a whole and not one of its attributes.
Since I'm not used to the template package either, I've created a small example:
type Category struct {
Title string
Count int
}
func main() {
tmpl, _ := template.Parse(`
{.repeated section Categories}
<p>{Title} ({Count})</p>
{.end}
`, nil)
categories := []Category{
Category{"Foo", 3},
Category{"Bar", 5},
}
tmpl.Execute(os.Stdout, map[string]interface{} {
"Categories": categories,
})
}