template包实现了数据驱动的用于生成文本输出的模板。
如果要生成HTML格式的输出,参见html/template包,该包提供了和本包相同的接口,但会自动将输出转化为安全的HTML格式输出,可以抵抗一些网络攻击。
通过将模板应用于一个数据结构(即该数据结构作为模板的参数)来执行,来获得输出。模板中的注释引用数据接口的元素(一般如结构体的字段或者字典的键)来控制执行过程和获取需要呈现的值。模板执行时会遍历结构并将指针表示为’.’(称之为”dot”)指向运行过程中数据结构的当前位置的值。
用作模板的输入文本必须是utf-8编码的文本。”Action”—数据运算和控制单位—由”{{“和”}}”界定;在Action之外的所有文本都不做修改的拷贝到输出中。Action内部不能有换行,但注释可以有换行。
经解析生成模板后,一个模板可以安全的并发执行。
简单的文本输出
func main() {type A struct {Name string}type B struct {Age int8A}var b = B{Age: 10,A: A{Name: "xx",},}tmpl, _ := template.New("test").Parse("age:{{.Age}},name:{{.A.Name}}")tmpl.Execute(os.Stdout, b)}------------------output------------------age:10,name:xx
使用map 进行渲染
除了使用struct ,还可以使用map 进行渲染,我们需要注意,在使用struct时字段必须是可以导出的
func main() {b := make(map[string]interface{})b["age"] = 10b["b"] = map[string]interface{}{"name": "xx"}tmpl, _ := template.New("test").Parse("age:{{.age}},name:{{.b.name}}")tmpl.Execute(os.Stdout, b)}------------------output------------------age:10,name:xx
模板文件
新建一个模板文件 demo.go.tpl,内容如下
age:{{.Age}},name:{{.A.Name}}
上面的代码改为:
func main() {type A struct {Name string}type B struct {Age int8A}var b = B{Age: 10,A: A{Name: "xx",},}tmpl, _ := template.New("test").ParseFiles("demo.go.tpl")tmpl.Execute(os.Stdout, b)}------------------output------------------age:10,name:xx
多模板
func main() {type A struct {Name string}type B struct {Age int8A}var b = B{Age: 10,A: A{Name: "xx",},}tmpl, _ := template.New("test01").Parse("test01:age:{{.Age}},name:{{.A.Name}}")tmpl, _ = tmpl.New("test02").Parse("test02:name:{{.A.Name}},age:{{.Age}}")tmpl.ExecuteTemplate(os.Stdout, "test01", b) // 指定模板fmt.Println()tmpl = tmpl.Lookup("test01") // 切换模板fmt.Println(tmpl.Name()) // test01}
循环
{{ range pipeline }} T1 {{ end }}*// 这个 else 比较有意思,如果 pipeline 的长度为 0 则输出 else 中的内容*{{ range pipeline }} T1 {{ else }} T0 {{ end }}*// 获取容器的下标*{{ range $index, $value := pipeline }} T1 {{ end }}
func main() {type Inventory struct {Material stringCount uint}type NewInventory struct {Fields []Inventory}sweaters := NewInventory{Fields: []Inventory{{Material: "wool", Count: 19},{Material: "wooltwo", Count: 20},}}var Text = `{{range .Fields }}Material: {{.Material}} - Count:{{.Count}}{{ end }}`// var Text = `//{{range $index, $value := .Fields }}// Material: {{$value.Material}} - Count:{{$value.Count}}//{{ end }}//`tmpl, _ := template.New("test").Parse(Text)tmpl.Execute(os.Stdout, sweaters)}------------------output------------------Material: wool - Count:19Material: wooltwo - Count:20
条件判断
{{if pipeline}} T1 {{end}}如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。{{ if pipeline }} T1 {{ else }} T0 {{ end }}{{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }}
func main() {b := make(map[string]interface{})b["age"] = 10b["b"] = map[string]interface{}{"name": "xxx"}tpl := `{{ if gt .age 100 }}age > 100{{ else }}age < 100{{ end }}`tmpl, _ := template.New("test").Parse(tpl)template.ParseFiles()tmpl.Execute(os.Stdout, b)}------------------output------------------age < 100
模板函数
可以对某个字段使用函数操作
func main() {funcMap := template.FuncMap{"title": strings.ToUpper,}const templateText = `Input: {{printf "%q" .}}Output 0: {{title .}}Output 1: {{title . | printf "%q"}}Output 2: {{printf "%q" . | title}}`tmpl, _ := template.New("titleTest").Funcs(funcMap).Parse(templateText)tmpl.Execute(os.Stdout, "the go programming language")}------------------output------------------Input: "the go programming language"Output 0: THE GO PROGRAMMING LANGUAGEOutput 1: "THE GO PROGRAMMING LANGUAGE"Output 2: "THE GO PROGRAMMING LANGUAGE"
内置函数
and : 返回第一个能转换成 false 的值,在 Go 中就是零值,如果都为 true 返回最后一个值。or : 返回第一个能转换成 true 的值,在 Go 中就是非零值,如果都为 false 返回最后一个值。not : 返回它的单个参数的布尔值的否定len : 返回复合类型的长度index : "index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。print : fmt.Sprintprintf : fmt.Sprintfprintln : fmt.Sprintlnhtml : 返回其参数文本表示的HTML逸码等价表示。urlquery : 返回其参数文本表示的可嵌入URL查询的逸码等价表示。js : 返回其参数文本表示的JavaScript逸码等价表示。call执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);其中Y是函数类型的字段或者字典的值,或者其他类似情况;call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;
比较函数
eq: ==ge: >=gt: >le: <=lt: <ne: !=
with
{{ with pipeline }} T1 {{ end }}// 如果 pipeline 是空值则输出 T0{{ with pipeline }} T1 {{ else }} T0 {{ end }}{{ with arg }}. // 此时 . 就是 arg{{ end }}
func main() {b := make(map[string]interface{})b["age"] = 10b["b"] = map[string]interface{}{"name": "xx"}const templateText = `{{ with .age }}{{ . }}{{ end }}`tmpl, _ := template.New("titleTest").Parse(templateText)tmpl.Execute(os.Stdout, b)}------------------output------------------10
变量
range 循环可以声明两个变量:
range $index, $element := pipeline
在 if、with 和 range 中,变量的作用域拓展到 {{ end }} 所在的位置。
如果不是控制结构,声明的变量的作用域会扩展到整个模板。
例如在模板开始时声明变量:
{{ $pages := .pagination.Pages }}{{ $current := .pagination.Current }}
在渲染开始的时候,$ 变量会被替换成 . 开头的值,例如 $pages 会被替换成 .pagenation.Pages。所以在模板间的相互引用不会传递变量,变量只在某个特定的作用域中产生作用。
func main() {b := make(map[string]interface{})b["age"] = 10tpl := `{{ $age := .age }}{{ $age }}`tmpl, _ := template.New("test").Parse(tpl)tmpl.Execute(os.Stdout, b)}------------------output------------------10
call
函数必须有一个或两个回值(第二个返回值必须是 error,如果值不为 nil 会停止模板渲染)
另外 函数调用后,只能返回一个结果
func main() {tpl := `{{ $res := call .x .y .z }}{{ $res }}`t, _ := template.New("test").Parse(tpl)t.Execute(os.Stdout, map[string]interface{}{"x": func(x, y int) (int, error) { return x + y, nil },"y": 2,"z": 3,})}------------------output------------------5
define & template
公共的部分可以使用define 定义,然后使用template 去引用
func main() {const templateText = `{{ define "module_name" }}this is public block{{ end }}{{template "module_name"}}{{template "module_name"}}{{template "module_name"}}`tmpl, _ := template.New("titleTest").Parse(templateText)tmpl.Execute(os.Stdout, nil)}
