template包实现了数据驱动的用于生成文本输出的模板。
如果要生成HTML格式的输出,参见html/template包,该包提供了和本包相同的接口,但会自动将输出转化为安全的HTML格式输出,可以抵抗一些网络攻击。
通过将模板应用于一个数据结构(即该数据结构作为模板的参数)来执行,来获得输出。模板中的注释引用数据接口的元素(一般如结构体的字段或者字典的键)来控制执行过程和获取需要呈现的值。模板执行时会遍历结构并将指针表示为’.’(称之为”dot”)指向运行过程中数据结构的当前位置的值。
用作模板的输入文本必须是utf-8编码的文本。”Action”—数据运算和控制单位—由”{{“和”}}”界定;在Action之外的所有文本都不做修改的拷贝到输出中。Action内部不能有换行,但注释可以有换行。
经解析生成模板后,一个模板可以安全的并发执行。

简单的文本输出

  1. func main() {
  2. type A struct {
  3. Name string
  4. }
  5. type B struct {
  6. Age int8
  7. A
  8. }
  9. var b = B{
  10. Age: 10,
  11. A: A{
  12. Name: "xx",
  13. },
  14. }
  15. tmpl, _ := template.New("test").Parse("age:{{.Age}},name:{{.A.Name}}")
  16. tmpl.Execute(os.Stdout, b)
  17. }
  18. ------------------output------------------
  19. age:10,name:xx

使用map 进行渲染

除了使用struct ,还可以使用map 进行渲染,我们需要注意,在使用struct时字段必须是可以导出的

  1. func main() {
  2. b := make(map[string]interface{})
  3. b["age"] = 10
  4. b["b"] = map[string]interface{}{"name": "xx"}
  5. tmpl, _ := template.New("test").Parse("age:{{.age}},name:{{.b.name}}")
  6. tmpl.Execute(os.Stdout, b)
  7. }
  8. ------------------output------------------
  9. age:10,name:xx

模板文件

新建一个模板文件 demo.go.tpl,内容如下

  1. age:{{.Age}},name:{{.A.Name}}

上面的代码改为:

  1. func main() {
  2. type A struct {
  3. Name string
  4. }
  5. type B struct {
  6. Age int8
  7. A
  8. }
  9. var b = B{
  10. Age: 10,
  11. A: A{
  12. Name: "xx",
  13. },
  14. }
  15. tmpl, _ := template.New("test").ParseFiles("demo.go.tpl")
  16. tmpl.Execute(os.Stdout, b)
  17. }
  18. ------------------output------------------
  19. age:10,name:xx

多模板

  1. func main() {
  2. type A struct {
  3. Name string
  4. }
  5. type B struct {
  6. Age int8
  7. A
  8. }
  9. var b = B{
  10. Age: 10,
  11. A: A{
  12. Name: "xx",
  13. },
  14. }
  15. tmpl, _ := template.New("test01").Parse("test01:age:{{.Age}},name:{{.A.Name}}")
  16. tmpl, _ = tmpl.New("test02").Parse("test02:name:{{.A.Name}},age:{{.Age}}")
  17. tmpl.ExecuteTemplate(os.Stdout, "test01", b) // 指定模板
  18. fmt.Println()
  19. tmpl = tmpl.Lookup("test01") // 切换模板
  20. fmt.Println(tmpl.Name()) // test01
  21. }

循环

  1. {{ range pipeline }} T1 {{ end }}
  2. *// 这个 else 比较有意思,如果 pipeline 的长度为 0 则输出 else 中的内容*
  3. {{ range pipeline }} T1 {{ else }} T0 {{ end }}
  4. *// 获取容器的下标*
  5. {{ range $index, $value := pipeline }} T1 {{ end }}
  1. func main() {
  2. type Inventory struct {
  3. Material string
  4. Count uint
  5. }
  6. type NewInventory struct {
  7. Fields []Inventory
  8. }
  9. sweaters := NewInventory{
  10. Fields: []Inventory{
  11. {Material: "wool", Count: 19},
  12. {Material: "wooltwo", Count: 20},
  13. }}
  14. var Text = `
  15. {{range .Fields }}
  16. Material: {{.Material}} - Count:{{.Count}}
  17. {{ end }}
  18. `
  19. // var Text = `
  20. //{{range $index, $value := .Fields }}
  21. // Material: {{$value.Material}} - Count:{{$value.Count}}
  22. //{{ end }}
  23. //`
  24. tmpl, _ := template.New("test").Parse(Text)
  25. tmpl.Execute(os.Stdout, sweaters)
  26. }
  27. ------------------output------------------
  28. Material: wool - Count:19
  29. Material: wooltwo - Count:20

条件判断

  1. {{if pipeline}} T1 {{end}}
  2. 如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。
  3. Empty值包括false0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
  4. {{ if pipeline }} T1 {{ else }} T0 {{ end }}
  5. {{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }}
  1. func main() {
  2. b := make(map[string]interface{})
  3. b["age"] = 10
  4. b["b"] = map[string]interface{}{"name": "xxx"}
  5. tpl := `
  6. {{ if gt .age 100 }}
  7. age > 100
  8. {{ else }}
  9. age < 100
  10. {{ end }}
  11. `
  12. tmpl, _ := template.New("test").Parse(tpl)
  13. template.ParseFiles()
  14. tmpl.Execute(os.Stdout, b)
  15. }
  16. ------------------output------------------
  17. age < 100

模板函数

可以对某个字段使用函数操作

  1. func main() {
  2. funcMap := template.FuncMap{
  3. "title": strings.ToUpper,
  4. }
  5. const templateText = `
  6. Input: {{printf "%q" .}}
  7. Output 0: {{title .}}
  8. Output 1: {{title . | printf "%q"}}
  9. Output 2: {{printf "%q" . | title}}
  10. `
  11. tmpl, _ := template.New("titleTest").Funcs(funcMap).Parse(templateText)
  12. tmpl.Execute(os.Stdout, "the go programming language")
  13. }
  14. ------------------output------------------
  15. Input: "the go programming language"
  16. Output 0: THE GO PROGRAMMING LANGUAGE
  17. Output 1: "THE GO PROGRAMMING LANGUAGE"
  18. Output 2: "THE GO PROGRAMMING LANGUAGE"

内置函数

  1. and : 返回第一个能转换成 false 的值,在 Go 中就是零值,如果都为 true 返回最后一个值。
  2. or : 返回第一个能转换成 true 的值,在 Go 中就是非零值,如果都为 false 返回最后一个值。
  3. not : 返回它的单个参数的布尔值的否定
  4. len : 返回复合类型的长度
  5. index : "index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
  6. print : fmt.Sprint
  7. printf : fmt.Sprintf
  8. println : fmt.Sprintln
  9. html : 返回其参数文本表示的HTML逸码等价表示。
  10. urlquery : 返回其参数文本表示的可嵌入URL查询的逸码等价表示。
  11. js : 返回其参数文本表示的JavaScript逸码等价表示。
  12. call
  13. 执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
  14. "call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);
  15. 其中Y是函数类型的字段或者字典的值,或者其他类似情况;
  16. call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
  17. 该函数类型值必须有12个返回值,如果有2个则后一个必须是error接口类型;
  18. 如果有2个返回值的方法返回的errornil,模板执行会中断并返回给调用模板执行者该错误;

比较函数

  1. eq: ==
  2. ge: >=
  3. gt: >
  4. le: <=
  5. lt: <
  6. ne: !=

with

  1. {{ with pipeline }} T1 {{ end }}
  2. // 如果 pipeline 是空值则输出 T0
  3. {{ with pipeline }} T1 {{ else }} T0 {{ end }}
  4. {{ with arg }}
  5. . // 此时 . 就是 arg
  6. {{ end }}
  1. func main() {
  2. b := make(map[string]interface{})
  3. b["age"] = 10
  4. b["b"] = map[string]interface{}{"name": "xx"}
  5. const templateText = `
  6. {{ with .age }}
  7. {{ . }}
  8. {{ end }}
  9. `
  10. tmpl, _ := template.New("titleTest").Parse(templateText)
  11. tmpl.Execute(os.Stdout, b)
  12. }
  13. ------------------output------------------
  14. 10

变量

range 循环可以声明两个变量:

  1. range $index, $element := pipeline

在 if、with 和 range 中,变量的作用域拓展到 {{ end }} 所在的位置。
如果不是控制结构,声明的变量的作用域会扩展到整个模板。

例如在模板开始时声明变量:

  1. {{ $pages := .pagination.Pages }}
  2. {{ $current := .pagination.Current }}

在渲染开始的时候,$ 变量会被替换成 . 开头的值,例如 $pages 会被替换成 .pagenation.Pages。所以在模板间的相互引用不会传递变量,变量只在某个特定的作用域中产生作用。

  1. func main() {
  2. b := make(map[string]interface{})
  3. b["age"] = 10
  4. tpl := `
  5. {{ $age := .age }}
  6. {{ $age }}
  7. `
  8. tmpl, _ := template.New("test").Parse(tpl)
  9. tmpl.Execute(os.Stdout, b)
  10. }
  11. ------------------output------------------
  12. 10

call

函数必须有一个或两个回值(第二个返回值必须是 error,如果值不为 nil 会停止模板渲染)
另外 函数调用后,只能返回一个结果

  1. func main() {
  2. tpl := `
  3. {{ $res := call .x .y .z }}
  4. {{ $res }}
  5. `
  6. t, _ := template.New("test").Parse(tpl)
  7. t.Execute(os.Stdout, map[string]interface{}{
  8. "x": func(x, y int) (int, error) { return x + y, nil },
  9. "y": 2,
  10. "z": 3,
  11. })
  12. }
  13. ------------------output------------------
  14. 5

define & template

公共的部分可以使用define 定义,然后使用template 去引用

  1. func main() {
  2. const templateText = `
  3. {{ define "module_name" }}
  4. this is public block
  5. {{ end }}
  6. {{template "module_name"}}
  7. {{template "module_name"}}
  8. {{template "module_name"}}
  9. `
  10. tmpl, _ := template.New("titleTest").Parse(templateText)
  11. tmpl.Execute(os.Stdout, nil)
  12. }