Edit This Page

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

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

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

The signature for EmailExpander is typically

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

/**
 * PrintEmails
 */

package main

import (
    "fmt"
    "os"
    "strings"
    "text/template"
)

type Person struct {
    Name   string
    Emails []string
}

const templ = `The name is {{.Name}}.
{{range .Emails}}
        An email is "{{. | emailExpand}}"
{{end}}
`

func EmailExpander(args ...interface{}) string {
    ok := false
    var s string
    if len(args) == 1 {
        s, ok = args[0].(string)
    }
    if !ok {
        s = fmt.Sprint(args...)
    }

    // find the @ symbol
    substrs := strings.Split(s, "@")
    if len(substrs) != 2 {
        return s
    }
    // replace the @ by " at "
    return (substrs[0] + " at " + substrs[1])
}

func main() {
    person := Person{
        Name:   "jan",
        Emails: []string{"[email protected]", "[email protected]"},
    }

    t := template.New("Person template")

    // add our function
    t = t.Funcs(template.FuncMap{"emailExpand": EmailExpander})

    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 is

The name is jan.

An email is "jan at newmarch.name"

An email is "jan.newmarch at gmail.com"