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

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,
})
}

Related

How to setup a template with multiple items for a list in ui5

in my UI5 App, there is a list, in which I want to show more than one DisplayListItem. Therefore I set up a template for the list to bind with
oList.bindAggregation("items", "/my_path", oListTemplate);
If i build the template like this:
oListTemplate = new sap.m.DisplayListItem(...);
Anything works perfekt.
But now I need to give several new sap.m.DisplayListItem(...) with an array like oListTempate = [new sap.m.DisplayListItem(...), new sap.m.DisplayListItem(...),..]; into one template. If I do so, I get an error for not having a template given:
Error: Missing template or factory function for aggregation items
Is it not possible to give more than one item with the template. At the sap docu: https://sapui5.hana.ondemand.com/1.34.9/docs/guide/91f057786f4d1014b6dd926db0e91070.html there is the line:
A template is not necessarily a single control as shown in the example above, but can also be a tree of controls.
Because of this, I think it is possible, but I don't know how to do it.
thank you in advance
Loop through your results and add a DisplayListItem for every result:
var aItems = [];
oResponse.results.forEach(function(oResult){
aItems.push(new sap.m.DisplayListItem({
label: "oResult.name"
})
);
});
Add your Array to your List:
var oList = new sap.m.List({
headerText: "Test list",
items: aItems
});
The correct way to specify the template is this:
oList.bindAggregation("items", {
path: "/my_path",
template: oListTemplate
});
Adding items to List by creating individual items may result in performance issues.

Template object field enforcement

Go provides great build in HTML templating functionality, however for me it is important that I can ensure certain fields will always be available to templates in my application. A good example of this is a title field, which needs to be shown on every HTML page.
Given the following pages:
Home
Register
Contact
I would likely create the following objects for the templating system:
HomePage struct
RegisterPage struct
ContactPage struct
Is there a recommended way to ensure that the structs for each page have certain fields available to them?
Ideally I would implement this through Polymorphism, but that isn't officially supported in Go. The alternative, embedding doesn't appear to have any enforcement, i.e all of the child structs can embed a parent struct but don't have to.
Let me know if I haven't expressed my question clearly enough.
Executing a template does not enforce anything to the parameters, Template.Execute() accepts a value of type interface{}.
You are the one creating the HomePage, RegisterPage and ContactPage structs. What stops you from embedding a BasePage struct with the required fields? Are you worried you will forget about it? You will notice it at the first testing, I wouldn't worry about that:
type BasePage struct {
Title string
Other string
// other required fields...
}
type HomePage struct {
BasePage
// other home page fields...
}
type RegisterPage struct {
BasePage
// other register page fields...
}
If you want from code to check if page structs embed the BasePage, I recommend another way: interfaces.
type HasBasePage interface {
GetBasePage() BasePage
}
Example HomePage that implements it:
type HomePage struct {
BasePage
// other home page fields...
}
func (h *HomePage) GetBasePage() BasePage {
return h.BasePage
}
Now obviously only pages that have a GetBasePage() method can be passed as a value of HasBasePage:
var page HasBasePage = &HomePage{} // Valid, HomePage implements HasBasePage
If you don't want to use interfaces, you can use the reflect package to check if a value is a struct value and if it embeds another interface. Embedded structs appear and can be accessed like ordinary fields e.g. with Value.FieldByName(), with the type name being the field name.
Example code using reflect to check if a value embeds BasePage:
page := &HomePage{BasePage: BasePage{Title: "Home page"}}
v := reflect.ValueOf(page)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
fmt.Println("Error: not struct!")
return
}
bptype := reflect.TypeOf(BasePage{})
bp := v.FieldByName(bptype.Name()) // "BasePage"
if !bp.IsValid() || bp.Type() != bptype {
fmt.Println("Error: struct does not embed BasePage!")
return
}
fmt.Printf("%+v", bp)
Output (try it on the Go Playground):
{Title:Home page Other:}

How do you pass multiple objects to go template?

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

How to write a boundHelper to iterate a map in Ember.js

I have a model which contains an Ember.Map, and I want to render the content of that map in a template.
I've tried using the custom bound helper below, but the template will not re-render as values are added/removed from the map.
Essentially I just want to replicate the behaviour of {{#each}} for a map.
Ember.Handlebars.registerBoundHelper('eachInMap', function(map, block) {
out = "";
map.forEach(function(k,v) {
out += block.fn(v)
});
return new Handlebars.SafeString(out);
}, /* what dependencies to put here? */);
Invoked by a template
{{#eachInMap myMap}} foo bar {{/eachInMap}}
Check out https://github.com/emberjs/ember.js/pull/2659.
Basically, boundHelpers don't currently support blocks sorry.
The current workaround is to create a non-bound helper and wrap it in a {{#bind}} block.

sencha touch :: how to create list of hbox-items

i want to generate a list of data out of a store. each data item holds a title and a boolean. now i want to have a list of the items, each holding the title and a togglefield (showing the boolean) on the right of the title.
how could I do that?
thnx!
From the Sencha Touch documentation, a List is "a mechanism for displaying data using a list layout template", i.e. html template. If you want a 'list' of components, you'll have to make your own extension of DataView (I think).
A workaround could be to put an html checkbox inside your itemTpl.
Something like (warning - not tested):
itemTpl: '<p>{title}: <input type="checkbox" name="BoolCheckbox" class="boolcheckbox"'
+ "{[(values.bool? 'checked="checked"' : '')]}"
+ '></input></p>'
To run your own code in the XTemplate, you bracket it with {[]}. When in this scope, you have access to a variable 'values', which contains the data for the record.
To detect events, you'd add a listener to the list:
itemtap: function (dataView, index, item, e) {
if (e.getTarget().getClass().toString() == "boolcheckbox") {
// do something
}
}
Some resources on templates:
http://dev.sencha.com/deploy/touch/docs/
http://www.sencha.com/learn/xtemplates-part-i/
http://www.sencha.com/learn/xtemplates-part-ii/