Jay
Jay

Reputation: 20156

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

Upvotes: 10

Views: 9267

Answers (3)

Rick Smith
Rick Smith

Reputation: 9251

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

Upvotes: 7

Gnani
Gnani

Reputation: 465

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

Upvotes: 1

Paul Hankin
Paul Hankin

Reputation: 58369

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

Upvotes: 5

Related Questions