Inserting object values

A template is applied to a Go object. Fields from that Go object can be inserted into the template, and you can 'dig' into the object to find subfields, etc. The current object is represented as '.', so that to insert the value of the current object as a string, you use {{.}}. The package uses the fmt package by default to work out the string used as inserted values.

To insert the value of a field of the current object, you use the field name prefixed by '.'. For example, if the object is of type

type Person struct {
        Name      string
        Age       int
        Emails     []string
        Jobs       []*Jobs
}

then you insert the values of Name and Age by

The name is {{.Name}}.
The age is {{.Age}}.

We can loop over the elements of an array or other list using the range command. So to access the contents of the Emails array we do

{{range .Emails}}
        ...
{{end}}

if Job is defined by

type Job struct {
    Employer string
    Role     string
}

and we want to access the fields of a Person's Jobs, we can do it as above with a {{range .Jobs}}. An alternative is to switch the current object to the Jobs field. This is done using the {{with ...}} ... {{end}} construction, where now {{.}} is the Jobs field, which is an array:

{{with .Jobs}}
    {{range .}}
        An employer is {{.Employer}}
        and the role is {{.Role}}
    {{end}}
{{end}}

You can use this with any field, not just an array. Using templates

Once we have a template, we can apply it to an object to generate a new string, using the object to fill in the template values. This is a two-step process which involves parsing the template and then applying it to an object. The result is output to a Writer, as in

t := template.New("Person template")
t, err := t.Parse(templ)
if err == nil {
    buff := bytes.NewBufferString("")
    t.Execute(buff, person)
}

An example program to apply a template to an object and print to standard output is

/**
 * PrintPerson
 */

package main

import (
    "fmt"
    "html/template"
    "os"
)

type Person struct {
    Name   string
    Age    int
    Emails []string
    Jobs   []*Job
}

type Job struct {
    Employer string
    Role     string
}

const templ = `The name is {{.Name}}.
The age is {{.Age}}.
{{range .Emails}}
        An email is {{.}}
{{end}}

{{with .Jobs}}
    {{range .}}
        An employer is {{.Employer}}
        and the role is {{.Role}}
    {{end}}
{{end}}
`

func main() {
    job1 := Job{Employer: "Monash", Role: "Honorary"}
    job2 := Job{Employer: "Box Hill", Role: "Head of HE"}

    person := Person{
        Name:   "jan",
        Age:    50,
        Emails: []string{"[email protected]", "[email protected]"},
        Jobs:   []*Job{&job1, &job2},
    }

    t := template.New("Person template")
    t, err := t.Parse(templ)
    checkError(err)

    err = t.Execute(os.Stdout, person)
    checkError(err)
}

func checkError(err error) {
    if err != nil {
        fmt.Println("Fatal error ", err.Error())
        os.Exit(1)
    }
}

The output from this is

The name is jan.
The age is 50.

An email is [email protected]

An email is [email protected]

An employer is Monash
and the role is Honorary

An employer is Box Hill
and the role is Head of HE

Note that there is plenty of whitespace as newlines in this printout. This is due to the whitespace we have in our template. If we wish to reduce this, eliminate newlines in the template as in

{{range .Emails}} An email is {{.}} {{end}}

In the example, we used a string in the program as the template. You can also load templates from a file using the function template.ParseFiles(). For some reason that I don't understand (and which wasn't required in earlier versions), the name assigned to the template must be the same as the basename of the first file in the list of files. Is this a bug?

results matching ""

    No results matching ""