Template

Template

  • Package template implements data-driven templates for generating textual output.
  • Templates are executed by applying them to a data structure. Annotations in the template refer to elements of the data structure (typically a field of a struct or a key in a map) to control execution and derive values to be displayed. Execution of the template walks the structure and sets the cursor, represented by a period ‘.’ and called “dot”, to the value at the current location in the structure as execution proceeds.

  • The input text for a template is UTF-8-encoded text in any format. “Actions”–data evaluations or control structures–are delimited by “{{” and “}}”; all text outside actions is copied to the output unchanged. Except for raw strings, actions may not span newlines, although comments can.

Pipelines

  • A pipeline is a possibly chained sequence of “commands”. A command is a simple value (argument) or a function or method call, possibly with multiple arguments.

Example of letter code

func main() {
	// Define a template.
	const letter = `
Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with .Gift -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
`

	// Prepare some data to insert into the template.
	type Recipient struct {
		Name, Gift string
		Attended   bool
	}
	var recipients = []Recipient{
		{"Aunt Mildred", "bone china tea set", true},
		{"Uncle John", "moleskin pants", false},
		{"Cousin Rodney", "", false},
	}

	// Create a new template and parse the letter into it.
	t := template.Must(template.New("letter").Parse(letter))

	// Execute the template for each recipient.
	for _, r := range recipients {
		err := t.Execute(os.Stdout, r)
		if err != nil {
			log.Println("executing template:", err)
		}
	}

}

Glob example

  • demonstrate loading a set of templates from a directory

    // templateFile defines the contents of a template to be stored in a file, for testing.
    type templateFile struct {
    	name     string
    	contents string
    }
    
    func createTestDir(files []templateFile) string {
    	dir, err := ioutil.TempDir("", "template")
    	if err != nil {
    		log.Fatal(err)
    	}
    	for _, file := range files {
    		f, err := os.Create(filepath.Join(dir, file.name))
    		if err != nil {
    			log.Fatal(err)
    		}
    		defer f.Close()
    		_, err = io.WriteString(f, file.contents)
    		if err != nil {
    			log.Fatal(err)
    		}
    	}
    	return dir
    }
    
    func main() {
    	// Here we create a temporary directory and populate it with our sample
    	// template definition files; usually the template files would already
    	// exist in some location known to the program.
    	dir := createTestDir([]templateFile{
    		// T0.tmpl is a plain template file that just invokes T1.
    		{"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
    		// T1.tmpl defines a template, T1 that invokes T2.
    		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
    		// T2.tmpl defines a template T2.
    		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
    	})
    	// Clean up after the test; another quirk of running as an example.
    	defer os.RemoveAll(dir)
    
    	// pattern is the glob pattern used to find all the template files.
    	pattern := filepath.Join(dir, "*.tmpl")
    
    	// Here starts the example proper.
    	// T0.tmpl is the first name matched, so it becomes the starting template,
    	// the value returned by ParseGlob.
    	tmpl := template.Must(template.ParseGlob(pattern))
    
    	err := tmpl.Execute(os.Stdout, nil)
    	if err != nil {
    		log.Fatalf("template execution: %s", err)
    	}
    }
    

Helper example

  • demonstrates one way to share some templates and use them in different contexts. In this variant we add multiple driver templates by hand to an existing bundle of templates.

    
    // templateFile defines the contents of a template to be stored in a file, for testing.
    type templateFile struct {
    	name     string
    	contents string
    }
    
    func createTestDir(files []templateFile) string {
    	dir, err := ioutil.TempDir("", "template")
    	if err != nil {
    		log.Fatal(err)
    	}
    	for _, file := range files {
    		f, err := os.Create(filepath.Join(dir, file.name))
    		if err != nil {
    			log.Fatal(err)
    		}
    		defer f.Close()
    		_, err = io.WriteString(f, file.contents)
    		if err != nil {
    			log.Fatal(err)
    		}
    	}
    	return dir
    }
    
    func main() {
    	// Here we create a temporary directory and populate it with our sample
    	// template definition files; usually the template files would already
    	// exist in some location known to the program.
    	dir := createTestDir([]templateFile{
    		// T1.tmpl defines a template, T1 that invokes T2.
    		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
    		// T2.tmpl defines a template T2.
    		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
    	})
    	// Clean up after the test; another quirk of running as an example.
    	defer os.RemoveAll(dir)
    
    	// pattern is the glob pattern used to find all the template files.
    	pattern := filepath.Join(dir, "*.tmpl")
    
    	// Here starts the example proper.
    	// Load the helpers.
    	templates := template.Must(template.ParseGlob(pattern))
    	// Add one driver template to the bunch; we do this with an explicit template definition.
    	_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
    	if err != nil {
    		log.Fatal("parsing driver1: ", err)
    	}
    	// Add another driver template.
    	_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
    	if err != nil {
    		log.Fatal("parsing driver2: ", err)
    	}
    	// We load all the templates before execution. This package does not require
    	// that behavior but html/template's escaping does, so it's a good habit.
    	err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
    	if err != nil {
    		log.Fatalf("driver1 execution: %s", err)
    	}
    	err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
    	if err != nil {
    		log.Fatalf("driver2 execution: %s", err)
    	}
    }