I recently went through How To Use Templates in Go to refresh my memory on Golang templates. I was reminded how great they are and learned several things along the way.
I learned that the below syntaxes are equivalent:
{{ . | len }}
{{ (len .) }}
Here is the program I wrote and tweaked along the way, to refresh my memory in the future:
package main
import (
"html/template"
"os"
"strings"
)
type Pet struct {
Name string
Sex string
Intact bool
Age string
Breed []string
}
var dogs = []Pet{
{
// This is why you should use html/template and not text/template when
// rendering HTML. Especially if you don't trust the source of your
// data!
//
// You don't want the template you render to do (potentially) malicious
// things in your user's webbrowser.
Name: "<script>alert(\"Gotcha!\");</script>Jujube",
Sex: "Female",
Intact: false,
Age: "10 months",
Breed: []string{
"German Shepherd",
"Pitbull",
},
},
{
Name: "Zephyr",
Sex: "Male",
Intact: true,
Age: "13 years, 3 months",
Breed: []string{
"German Shepherd",
"Border Collie",
},
},
{
Name: "Roger",
Sex: "Male",
Intact: false,
Age: "9 years, 7 months",
Breed: []string{
"German Shepherd",
"Border Collie",
},
},
{
Name: "Missi",
Sex: "Female",
Intact: false,
Age: "99 years, 2 months",
Breed: []string{
"Teacup Poodle",
},
},
}
// This is how you add functions to your template. You can add functions from
// other packages or define them inline here.
//
// See petsHtml.tmpl for an example of using "join".
var funcMap = template.FuncMap{
"dec": func(i int) int { return i - 1 }, // Inline!
"replace": strings.ReplaceAll, // Other package!
"join": strings.Join,
}
func main() {
// Create Template, add functions, and load template files.
tmpl, err := template.New("").Funcs(funcMap).ParseGlob("*.tmpl")
if err != nil {
panic(err)
}
// Create a file.
var f *os.File
f, err = os.Create("pets.html")
if err != nil {
panic(err)
}
defer f.Close()
// Execute a template by name with data "dogs" and write to writer "f".
err = tmpl.ExecuteTemplate(f, "petsHtml.tmpl", dogs)
if err != nil {
panic(err)
}
}
And here are the files I wrote and tweaked along the way:
pets.tmpl:
Number of dogs: {{ . | len }}
{{- range . }}
---
Name: {{ .Name }}
Sex: {{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if (eq .Sex "Female") }}spayed{{ else }}neutered{{ end }}{{ end }})
Age: {{ .Age }}
Breed: {{ join .Breed " & "}} ({{ if (len .Breed | eq 1) }}purebread{{ else }}mixed breed{{ end }})
{{ end }}
lastPet.tmpl:
{{- range (len . | dec | slice . ) }}
---
Name: {{ .Name }}
Sex: {{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if ("Female" | eq .Sex) }}spayed{{ else }}neutered{{ end }}{{ end }})
Age: {{ .Age }}
Breed: {{ replace .Breed "/" " & " }}
{{ end -}}
petsHtml.tmpl:
<p><strong>Pets:</strong> {{ . | len }}</p>
{{ range . }}
<hr />
<dl>
<dt>Name</dt>
<dd>{{ .Name }}</dd>
<dt>Sex</dt>
<dd>{{ .Sex }} ({{ if .Intact }}intact{{ else }}{{ if (eq .Sex "Female") }}spayed{{ else }}neutered{{ end }}{{ end }})</dd>
<dt>Age</dt>
<dd>{{ .Age }}</dd>
<dt>Breed</dt>
<dd>{{ join .Breed " & "}} ({{ if (len .Breed | eq 1) }}purebread{{ else }}mixed breed{{ end }})
</dl>
{{ end }}