Defining functions

The templates use the string representation of an object to insert values, using the fmt package to convert the object to a string. Sometimes this isn’t what is needed. For example, to avoid spammers getting hold of email addresses it is quite common to see the symbol '@' replaced by the word " at ", as in "jan at newmarch.name". If we want to use a template to display email addresses in that form, then we have to build a custom function to do this transformation.

Each template function has a name that is used in the templates themselves, and an associated Go function. These are linked by the type

  1. type FuncMap map[string]interface{}

For example, if we want our template function to be "emailExpand" which is linked to the Go function EmailExpander then we add this to the functions in a template by

  1. t = t.Funcs(template.FuncMap{"emailExpand": EmailExpander})

The signature for EmailExpander is typically

  1. func EmailExpander(args ...interface{}) string

In the use we are interested in, there should only be one argument to the function which will be a string. Existing functions in the Go template library have some initial code to handle non-conforming cases, so we just copy that. Then it is just simple string manipulation to change the format of the email address. A program is

  1. /**
  2. * PrintEmails
  3. */
  4. package main
  5. import (
  6. "fmt"
  7. "os"
  8. "strings"
  9. "text/template"
  10. )
  11. type Person struct {
  12. Name string
  13. Emails []string
  14. }
  15. const templ = `The name is {{.Name}}.
  16. {{range .Emails}}
  17. An email is "{{. | emailExpand}}"
  18. {{end}}
  19. `
  20. func EmailExpander(args ...interface{}) string {
  21. ok := false
  22. var s string
  23. if len(args) == 1 {
  24. s, ok = args[0].(string)
  25. }
  26. if !ok {
  27. s = fmt.Sprint(args...)
  28. }
  29. // find the @ symbol
  30. substrs := strings.Split(s, "@")
  31. if len(substrs) != 2 {
  32. return s
  33. }
  34. // replace the @ by " at "
  35. return (substrs[0] + " at " + substrs[1])
  36. }
  37. func main() {
  38. person := Person{
  39. Name: "jan",
  40. Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"},
  41. }
  42. t := template.New("Person template")
  43. // add our function
  44. t = t.Funcs(template.FuncMap{"emailExpand": EmailExpander})
  45. t, err := t.Parse(templ)
  46. checkError(err)
  47. err = t.Execute(os.Stdout, person)
  48. checkError(err)
  49. }
  50. func checkError(err error) {
  51. if err != nil {
  52. fmt.Println("Fatal error ", err.Error())
  53. os.Exit(1)
  54. }
  55. }

The output is

  1. The name is jan.
  2. An email is "jan at newmarch.name"
  3. An email is "jan.newmarch at gmail.com"