template在Go语言中的定位
- 模板作用,类似于
smarty
模板引擎,有自己的模板语法 - 支持
text/template
和html/template
,后者是在前者的基础上面,考虑了很多HTML代码注入安全方面的风险,通常是将HTML标签实体化;同时两者的目标语法都是一致的! - 模板核心内容有注释、行为(参数、管道,迭代,变量、比较)、函数、模板命名和嵌套等
1. template/text
- 基于
{{}}
,以及.
- 模板部分多采用"`“符号
- 支持条件判断
- 支持array、slice、map的range迭代
- 支持变量初始化声明和赋值
- 支持方法调用
- 支持函数调用
1.1. 示例
模板使用模式,再复杂的例子都是类似:
1
2
3
4
5
6
7
8
9
10
11
12
13
| type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
// 模板创建+解析文本模板
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil { panic(err) }
// 基于被解析的模板tmpl,应用指定的模板数据,并写到指定的输出
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
|
模板语法
2. 文本和空格删除
1
2
3
4
5
| tmpl, _ := template.New("test").Parse(`{{"prefix" -}} del-left < del-right {{- "suffix"}}`)
_ = tmpl.Execute(os.Stdout, nil)
// 输出
prefixdel-left < del-rightsuffix
|
2.1. 模板行为 - Actions
参数“arguments”和管线“pipelines”是对应数据的求值操作处理,除此外variable
也是一块!
pipeline
下面可以直接理解为输出管道,{{pipeline}}
理解为从管道输出copy出来的值!
2.1.1. 注释
1
2
3
4
| {{/* a comment */}}
//两边删除空格注释
{{- /* a comment with white space trimmed from preceding and following text */ -}}
|
2.1.2. 格式输出值(类似fmt.Print)
2.1.3. 条件判断 - IF END
空值代表false,0,nil指针,nil接口等空值
1
2
3
4
| {{if pipeline}} T1 {{end}}
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
|
2.1.4. 数据迭代 - Range END
可被pipeline
内容需要为array, slice, map, or channel,若pipleline长度为0,则不做输出!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // pipline长度为0,T1不会执行;若成功迭代,“.”代表被迭代的元素,并且T1执行
// 若值是map并且key是可比较的基础类型,元素将按key顺序访问
{{range pipeline}} T1 {{end}}
// 长度为0,执行T0
{{range pipeline}} T1 {{else}} T0 {{end}}
// kv迭代,针对map和slice、array支持kv变量迭代
{{range $k, $v := pipeline}}
{{$k}} : {{$v}}
{{end}}
// range迭代map、slice只有一个变量情况下,为迭代的元素的值!(这点特别注意,与go语法相反)
{{range $v := pipeline}}
{{$v}}
{{end}}
|
2.4. 自定义目标变量 - Variables
1
2
3
4
5
6
7
8
9
10
| // 自定义一个$list变量,其pipeline由.prod.size数组输出
{{$list := .prod.size}}
{{range $list}}
{{.}}
{{end}}
// 定义模板变量和赋值目标变量
{{$variable := pipeline}}
{{$variable = pipeline}}
|
2.1.5. 模板执行
1
2
3
4
5
| // 指定名称的模板执行
{{template "name"}}
// 附带pipeline数据执行name模板,在name模板中,"."代表pipeline的值
{{template "name" pipeline}}
|
2.1.6. 自定义模板
自定义模板通常定义一系列根模板,然后在其他模板块中使用
1
2
3
4
5
6
| // 内部定义一个name模板,并以数据执行它
{{block "name" pipeline}} T1 {{end}}
// 等价于
{{define "name"}} T1 {{end}} # 定义模板
{{template "name" pipeline}} # 执行模板
|
2.1.7. with操作
1
2
3
4
5
| // 类似与if,当pipeline不为空,则执行T1
{{with pipeline}} T1 {{end}}
// 类似于if..else..end
{{with pipeline}} T1 {{else}} T0 {{end}}
|
2.2. Arguments:模板参数
- A boolean, string, character, integer, floating-point, imaginary or complex constant in Go syntax
- nil关键字
- “.",代表结果的值
- “$name”,代表变量名称
- “.Field”:数据项的值
- “.Field1.Filed2”:数据项链 或者 “$x.Field1.Field2”
- “.Key”:数据的key,必须为map类型数据
- 支持链:".Field1.Key1.Field2.Key2”
- 不像其他Field需要大写,key在变量上支持小写:"$x.key1.key2”
- “.Method”:使用".“作为接收器,执行”.Method()"
- 这类方法必须要有一个确定的值类型返回,或者一个值+err返回(如果出错,执行中断,错误返回给调用者)
- 方法支持链式调用:".Field1.Key1.Method1.Field2.Key2.Method2"
- 方法支持变量调用:"$x.Method1.Field"
- “fun”
- 支持实例项调用
- “print (.F1 arg1) (.F2 arg2)”
- “(.StructValuedMethod “arg”).Field”
使用已定义的call函数在下面。
2.3. Pipelines
通道是一串命令,是一个简单的参数,或函数或方法调用,通道利用"|“字符分割。
2.5. Action、Arguments、Variable、Pipelines示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| {{"\"output\""}}
A string constant.
{{`"output"`}}
A raw string constant.
{{printf "%q" "output"}}
A function call.
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous
command.
{{printf "%q" (print "out" "put")}}
A parenthesized argument.(带括号的参数)
{{"put" | printf "%s%s" "out" | printf "%q"}}
A more elaborate call.
{{"output" | printf "%s" | printf "%q"}}
A longer chain.
{{with "output"}}{{printf "%q" .}}{{end}}
A with action using dot.
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
A with action that creates and uses a variable.
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
A with action that uses the variable in another action.
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
The same, but pipelined.
|
3. Functions函数
执行函数有两处:模板函数、全局函数
3.1. 预定义的全局函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // 关系类型函数
- and:"and x y"
- not: "not x"
- or: "or x y"
// 打印类
- print: fmt.Sprint别名
- printf: {{ printf "%-20v" data }} // 注意没有,
- println: {{ println . }} // 调试打印
// 索引值相关
- index: 返回索引值,"index x 1 2 3"
- len: 变量长度
// 其他
- call: "call .X.Y 1 2"、dot.X.Y(1, 2)
- js: 返回js转义值
- urlquery:表单套件增强,用于URL查询 (在html/template中存在异常,不要使用)
- html: 返回转义HTML(在html/template中存在异常,不要使用)
|
3.2. 自定义模板函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| const templ = `{{.TotalCount}} issues:
{{range .Items}}----------------------------------------
Number: {{.Number}}
User: {{.User.Login}}
Title: {{.Title | printf "%.64s"}}
Age: {{.CreatedAt | daysAgo}} days
{{end}}`
func daysAgo(t time.Time) int {
return int(time.Since(t).Hours() / 24)
}
// template.New先创建并返回一个模板;Funcs方法将daysAgo等自定义函数注册到模板中,并返回模板;最后调用Parse函数分析模板。
report, err := template.New("report").
Funcs(template.FuncMap{"daysAgo": daysAgo}).
Parse(templ)
if err != nil {
log.Fatal(err)
}
|
3.3. 比较类函数
1
2
3
4
5
6
7
8
9
10
| // 条件判断函数
- eq arg1 arg2 :等价于 arg1 == arg2
- ne
- lt
- le
- gt
- ge
// 多值条件判断
arg1==arg2 || arg1==arg3 || arg1==arg4 ...
|
4. 模板嵌套和执行
4.1. 嵌套
1
2
3
4
5
6
7
8
| // 目标定义
`{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}`
// 目标执行后
ONE TWO
|
4.2. 执行
1
2
3
4
5
6
7
8
9
10
11
| // 调用全部
err := tmpl.Execute(os.Stdout, "no data needed")
if err != nil {
log.Fatalf("execution failed: %s", err)
}
// 调用部分
err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
if err != nil {
log.Fatalf("execution failed: %s", err)
}
|
5. 其他文本格式输出相关的
- text/tabwriter
- text/template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // 表格输出
func textTableShow(w io.Writer, data map[string]interface{}) {
const format = "%v\t%v\n"
tw := new(tabwriter.Writer).Init(w, 0, 8, 2, ' ', 0)
fmt.Fprintf(tw, format, "db.Stats Key", "Values")
fmt.Fprintf(tw, format, "----------", "-----")
for k, v := range data {
fmt.Fprintf(tw, format, k, v)
}
tw.Flush()
}
// 文本模板输出
func textTmplShow(w io.Writer, data map[string]interface{}) {
// tmpl
const tmplTxt = `DB STATUS
-----------------------{{range $k, $v := .}}
{{printf "%-18v" $k}} => {{printf "%v" .}}{{end}}
`
tmpl, err := template.New("showStatus").Parse(tmplTxt)
if err != nil {
ErrHandle(err, "template.Parse")
}
if err := tmpl.Execute(w, data); err != nil {
ErrHandle(err, "template.Execute")
}
}
|