10

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...
Jay
  • 19,649
  • 38
  • 121
  • 184
  • Would http://stackoverflow.com/a/19548019/6309 help? – VonC May 22 '14 at 09:00
  • Using a struct as pipeline solves simple cases. More general/complex cases become cumbersome, see related question http://stackoverflow.com/questions/18276173/calling-a-template-with-several-pipeline-parameters – Deleplace May 22 '14 at 14:17

3 Answers3

7

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 ;)

Rick Smith
  • 9,031
  • 15
  • 81
  • 85
5

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}}
Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
  • I get that you can make a struct of struct, but doesn't this mean you will have to make a struct for every single web page in your system? What happens if you have 100 pages it seems a bit unmanageable? What naming system would you use? ie HomePageTemplate has HomePageStruct, etc... – Jay May 22 '14 at 09:58
  • 4
    @Jacob You don't need structs. You can also use a `map[string]interface{}`. – BurntSushi5 May 23 '14 at 21:15
  • tbh it isn't really wrong to have a struct for page. It can indeed make the data better documented, hence better managed. Moreover, you can use struct composition or just reuse pages with the same args. – SOFe Jun 24 '18 at 12:47
1

It depends on what your data is. I want to categorise this.

  1. 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.
  2. 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

Gnani
  • 455
  • 5
  • 18
  • This looks *very* dangerous in a webapp because unfortunatly the resulting Template is not thread-safe and users will accidently share the same User func. See http://play.golang.org/p/b5WVlUlGjS and also http://stackoverflow.com/questions/18302853/request-context-in-a-go-template#answer-18302879 – Deleplace May 22 '14 at 14:14
  • @Ripounet You are right. Thanks for pointing it out. But I used to do a clone on the template before appending the function map. You can do this if you append a dummy func Map previously on parsing of the template (jst like you did). See http://play.golang.org/p/hvHGJPwrpN. I want to know if this is a good way of doing it. Thanks again. Though im not sure how to edit the answer without causing confusion – Gnani May 22 '14 at 16:15
  • good catch, I had not thought of cloning the template. It would be interesting to measure the performance impact. The cloning work is described in source http://golang.org/src/pkg/html/template/template.go – Deleplace May 22 '14 at 17:13
  • Yea, performance is the part I'm worried too... Haven't checked it out yet. – Gnani May 22 '14 at 17:15
  • 1
    I benched on my workstation, the cloning adds a global 12% overhead in the program execution time. Not cloning: http://play.golang.org/p/YVLMd8vb9- , cloning: http://play.golang.org/p/rR5pWBar8O – Deleplace May 23 '14 at 07:52