template包实现了数据驱动的用于生成文本输出的模板。
如果要生成HTML格式的输出,参见html/template包,该包提供了和本包相同的接口,但会自动将输出转化为安全的HTML格式输出,可以抵抗一些网络攻击。
通过将模板应用于一个数据结构(即该数据结构作为模板的参数)来执行,来获得输出。模板中的注释引用数据接口的元素(一般如结构体的字段或者字典的键)来控制执行过程和获取需要呈现的值。模板执行时会遍历结构并将指针表示为’.’(称之为”dot”)指向运行过程中数据结构的当前位置的值。
用作模板的输入文本必须是utf-8编码的文本。”Action”—数据运算和控制单位—由”{{“和”}}”界定;在Action之外的所有文本都不做修改的拷贝到输出中。Action内部不能有换行,但注释可以有换行。
经解析生成模板后,一个模板可以安全的并发执行。
简单的文本输出
func main() {
type A struct {
Name string
}
type B struct {
Age int8
A
}
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"] = 10
b["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 int8
A
}
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 int8
A
}
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 string
Count 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:19
Material: 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"] = 10
b["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 LANGUAGE
Output 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.Sprint
printf : fmt.Sprintf
println : fmt.Sprintln
html : 返回其参数文本表示的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"] = 10
b["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"] = 10
tpl := `
{{ $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)
}