Templates

Data-driven templates for generating textual output.

type Stuff struct {
	Material string
	Count    uint
}
foo := Stuff{"wool", 17}
tmpl, _ := template.New("test").Parse("These {{.Count}} items are {{.Material}}")
tmpl.Execute(os.Stdout, foo)

Stdout: "These 17 items are wool"

Nested Templates

{{ template "header" .App }}

{{ template "main" . }}

{{ template "footer" }}

Data @ Multiple Structs

Compose the requiste data struct, which can be anonymous, by nesting whatever structs are necessary to fulfill template requirements, declaring which elements are applied where, using the template-dot notation, e.g., {{.Page.Name}}.

<h1>{{.Page.Name}}</h1>
<h2>Wisdom</h2>
<ul>
    {{if .Wisdom}}
        {{range .Wisdom}}
        <li>{{.Name}} - {{.Motto}}</li>
        {{end}}
    {{end}}

</ul>
<h2>Transport</h2>
<ul>
    {{range .Transport}}
    <li>{{.Name}} - {{.Model}} - {{.Doors}}</li>
    {{end}}
</ul>
data := struct {
    Page      pg
    Wisdom    *[]sage
    Transport []car
}{
    page,
    &sages,
    cars,
}

b := &strings.Builder{}
tpls.ExecuteTemplate(b, page.fname+".gohtml", data)

Most Common Functions

Parse Template(s)

Read/parse template file(s) per GLOB @ dir

paths = filepath.Join(dir, "templates", "*")
tpls = template.Must(template.ParseGlob(paths))

Read/parse a template (@ path)

path = filepath.Join(dir, "templates", "foo.tpl")
tpl = template.Must(template.ParseFiles(path))

Read a template file into a slice ([]byte)

data, err := ioutil.ReadFile(tplPath)
chk(err)

Parse a string

tpl, err = template.New(tplPath).Parse(string(data))
chk(err)

Execute Parsed Template(s) (tpl *Template)

@ tpl is one parsed template file

tpl.Execute(out,data)
Execute() signature
func (tpl *Template) Execute(w io.Writer, data interface{}) error

@ tpl is several parsed template files.

tpl.ExecuteTemplate(out, tplName, data)
ExecuteTemplate() signature
func (tpl *Template) ExecuteTemplate(w io.Writer, tplName string, data interface{}) error

==== Goes To Eleven ====

Passing Data To Templates

You get to pass in one value - that's it!

Fortunately, we have many different types which that value can be including composite types which compose together values. (These are also known as aggregate data types - they aggregate together many different values).

Slice

Use this for passing in a bunch of values of the same type. We could have a []int or a []string or a slice of any type.

Map

Use this for passing in key-value data.

Struct

This is probably the most commonly used data type when passing data to templates. A struct allows you to compose together values of different types.

Template variables

template variables

ASSIGN

{{$wisdom := .}}

USE

{{$wisdom}}

A pipeline inside an action may initialize a variable to capture the result. The initialization has syntax

$variable := pipeline

where $variable is the name of the variable. An action that declares a variable produces no output.

If a "range" action initializes a variable, the variable is set to the successive elements of the iteration. Also, a "range" may declare two variables, separated by a comma:

range $index, $element := pipeline

in which case $index and $element are set to the successive values of the array/slice index or map key and element, respectively. Note that if there is only one variable, it is assigned the element; this is opposite to the convention in Go range clauses.

A variable's scope extends to the "end" action of the control structure ("if", "with", or "range") in which it is declared, or to the end of the template if there is no such control structure. A template invocation does not inherit variables from the point of its invocation.

When execution begins, $ is set to the data argument passed to Execute, that is, to the starting value of dot.

Using functions in templates

template function documentation


template.FuncMap

FuncMap is the type of the map defining the mapping from names to functions. Each function must have either a single return value, or two return values of which the second has type error. In that case, if the second (error) return value evaluates to non-nil during execution, execution terminates and Execute returns that error.

template.Funcs

func (t *Template) Funcs(funcMap FuncMap) *Template

During execution functions are found in two function maps: - first in the template, - then in the global function map.

By default, no functions are defined in the template but the Funcs method can be used to add them.

Predefined global functions are defined in text/template.

Global Functions

There are "predefined global functions" which you can use.

You can read about these functions here

The following code samples will demonstrate some of these "predefined global functions":

Nested templates

nested templates documentation

define:

{{define "TemplateName"}}
insert content here
{{end}}

use:

{{template "TemplateName"}}

Passing data to templates

011_composition-and-methods

These files provide you with more examples of passing data to templates.

These files use the composition design pattern. You should favor this design pattern.

Read more about composition with Go here.

Hands-on exercises

These hands-on exercises will help you learn how to pass data to templates.

I have found that many students need practice with passing data to templates.

Take-away

One of the main take-aways is to use a composite data type. Often this data type will be a struct. Build a struct to hold the different pieces of data you'd like to pass to your template, then pass that to your template.