Advanced Template Usage
The template engine follows Go's text/template syntax. These patterns are useful when values are nested or some content is optional.
Nested data and loop scope
Inside range, . refers to the current item. Use $ to access the root value from a nested loop:
{{range .Departments}}
Department: {{.Name}}
{{range .People}}
- {{.Name}} ({{$.ReportTitle}})
{{end}}
{{end}}Multiple line ranges
A range or end action on its own line can leave an empty paragraph after it is evaluated. Avoid putting control actions in otherwise empty paragraphs when the extra spacing is unwanted. See For Loops with range.
For this template, Departments contains a slice of department values, each with a Name and a People slice. The root ReportTitle remains accessible as $.ReportTitle inside both loops.
Optional values
Use if to include text only when a value is present:
{{if .Notes}}Notes: {{.Notes}}{{end}}Go templates treat false, zero values, nil values, and empty strings, slices, maps, or arrays as empty in an if action. Use with when you want to change the current dot to a nested value:
{{with .Customer}}
Customer: {{.Name}}
{{end}}Word-specific considerations
- Use straight ASCII quotes around string arguments. Word may convert typed double quotes to curly quotes, which Go templates cannot parse. Backticks can quote string arguments too. See Basic Actions.
- Word may divide visible text across internal XML runs. If an expression fails to parse or match as expected, keep the complete action together in one text region and test the generated document in Word.
Low-level API examples
The low-level APIs let you add template functions, transform XML parts before or after templating, and inspect which variables a template uses.
Add a custom template function
Register functions with AddTemplateFuncs, then call them from the template. A custom function with the same name as an existing function replaces it.
import (
"strings"
"text/template"
)
docxTemplate.AddTemplateFuncs(template.FuncMap{
"upper": strings.ToUpper,
})Use it in the DOCX template:
{{upper .Title}}Create your own docx processing functions with pre and post-processors
Pre-processors run before template values are applied. Post-processors run after the generated DOCX has been assembled. Each handler receives an XML part as a string and returns the updated string or an error to be displayed if != nil.
import (
"strings"
"github.com/JJJJJJack/go-template-docx/xml"
)
docxTemplate.AddPreProcessors(
xml.HandlersMap{
"word/document.xml": {
func(content string) (string, error) {
return strings.ReplaceAll(content, "DRAFT", "FINAL"), nil
},
},
},
)Use AddPostProcessors with the same handler structure for changes that should happen after templating:
docxTemplate.AddPostProcessors(
xml.HandlersMap{
"word/document.xml": {
func(content string) (string, error) {
return strings.ReplaceAll(content, "internal-marker", ""), nil
},
},
},
)The map key is the path of a part inside the DOCX ZIP package (if you replace the extension .docx with .zip you can see the file structure inside), such as word/document.xml or word/header1.xml. Multiple handlers for a part run in slice order. Pass multiple maps in one call when configuring multiple processor maps; a later call to AddPreProcessors or AddPostProcessors replaces the previously configured maps.
These handlers operate on serialized XML, not on Word paragraphs or runs. A literal string may be escaped or split across XML elements, so direct string replacement is suitable only when the target appears exactly as expected in the XML.
Inspect template variables
GetTemplateVariables is useful for checking which variable expressions are present in the source DOCX before applying values:
import "fmt"
variables, err := docxTemplate.GetTemplateVariables()
if err != nil {
return err
}
if _, found := variables[".Title"]; !found {
return fmt.Errorf("template does not use .Title")
}The method returns a map[string]struct{} and an error if a DOCX part cannot be read or parsed as a template.