Golang Template
Get Started
初识模板
模板是go编程里一项重要的技术,模板是进行生成代码(类似于其他语言的宏编程),web页面后端渲染,文件批量生成与替换等应用的高效技术.模板语法
Text and Space
模板采用`{{Action}}`包含用户的Action,用户通过Action可以得到生成的动态数据,而编写模板时一般为了美观,会进行空格,换行,然而这样往往会导致生成的数据里也包含了不必要的数据,例如下面代码(包含了我们主要用于美观代码而使用的换行)
//解释 {{- -}}的用处
package main
import (
"html/template"
"os"
)
type Record struct {
Author string
Title string
}
func main() {
//我们想得到Author:Title这样的输出
//显然下面的方法输出不符合要求(包含了用于代码美观的空格)
/*templateStr := `
{{.Author}}:
{{.Title}}
`*/
//fix it: 方法一
//(省略多余的空格,换行,但对于复杂的代码可能会影响代码美观)
/*
templateStr := `{{.Author}}:{{.Title}}`
*/
// fix it: 方法二
// 使用了go提供的 `{{- }}`, `{{ -}}`, `{{- -}}`用于trim左右空白
/*
templateStr := `
{{- .Author }}:
{{- .Title -}}
`
*/
templ,err := template.New("template").Parse(templateStr)
if err != nil {
panic(err)
}
templ.Execute(os.Stdout,Record {
Author: "Marco Epsilon",
Title: "Learninng for Go Template",
})
}
`{{- -}}`主要用于trim左右的空白,我们可以很方便的使用其处理用于代码美化和用户数据造成的歧义性,关于是否将Action之间的空白都当成是代码美化,用`{{- -}}`消除歧义,
`{{print '\n'}}`生成真正的空白,还是综合运用,这就见仁见智了.
控制语句
通过go内置的Action我们可以在模板内进行逻辑判断,循环// template中的 if else range with
package main
import (
"os"
"html/template"
)
type Author struct {
Name string
Communication []string
IsWoman bool
Address string
}
func main() {
//if 语句
/*
{{if pipeline}}
//Your Code
{{end}}
{{if pipeline}}
//Your Code
{{else}}
//Your Another Code
{{end}}
{{if pipeline}}
//Your Code
{{else if pipeline}}
//Your Other Code
{{end}}
*/
//range 语句
// range语句用于遍历 map slice array
/*
{{range pipeline}}
Your Code
{{end}}
{{range $index,$element := pipeline}}
Your Code
{{end}}
*/
//with 语句
//with语句用于判断pipeline是否是nil
/*
{{with pipeline}}
Your Code
{{end}}
{{with pipeline}}
Your Code
{{else}}
Your Another Code
{{end}}
*/
templateStr := `
{{- "Author Name:"}} {{ .Name }}
{{- if .IsWoman }}
{{- print "\n" -}}
{{- "Sex: 女"}}
{{- else}}
{{- print "\n" -}}
{{- "Sex: 男"}}
{{- end }}
{{- range .Communication -}}
{{- print "\n" -}}
{{- "Contact by:"}} {{.}}
{{- end}}
{{- with .Address -}}
{{- print "\n" -}}
{{- "He offer address"}}: {{.}}
{{- end -}}
`
templ,err := template.New("template").Parse(templateStr)
if err != nil {
panic(err)
}
templ.Execute(os.Stdout,Author {
Name: "Marco Epsilon",
Communication: []string {"Github","Email","Wechat"},
IsWoman: false,
//Address: "Earth",
})
}
变量(Variables)
Go Template允许我们在其中定义变量,变量以`$varName`标识,变量可以`{{$varName = Value}}`进行赋值,变量拥有作用域,一般在控制语句`{{end}}`后或模板的结束,子模板不会继承调用点的变量,模板调用时 `$`被设置为执行时传入的参数// example for definition of variables
package main
import (
"os"
"html/template"
)
type Todos struct {
Titles []string
}
func main() {
templateStr := `
{{- range $index,$element := $.Titles -}}
{{$index}}:{{$element}}
{{- print "\n" -}}
{{- end -}}
`
templ,err := template.New("template").Parse(templateStr)
if err != nil {
panic(err)
}
templ.Execute(os.Stdout,Todos {
Titles: []string {
"Get Up!",
"Eat Breakfast",
"Do Sports",
"Eat Dinner",
},
})
}
函数(Functions)
Go Template提供了大量内置函数,当然也支持自定义函数内置函数
格式化类
print
printf
println
逻辑类
eq
ne
gt
ge
lt
le
转义类
html
js
urlquery
辅助类
index
len
call
and
not
or
自定义函数
虽然go template提供了很多内置函数,但是随着业务的复杂性提高,我们可以在template中使用自定义函数满足我们的需求,我们执行 go doc template.FuncMap
查看输出会发现template.FuncMap只是`map[string]interface{}`的别名
type FuncMap map[string]interface{}
所以使用自定义函数至少看起来像这样
package main
import (
"text/template"
"os"
)
func add(x int,y int) (int) {
return x + y;
}
func main() {
templateStr := `
{{- "add 1 2 result is: " -}}
{{- add 1 2 -}}
`
selfFuncs := template.FuncMap {
"add": add,
}
templ,err := template.New("funcExample").Funcs(selfFuncs).Parse(templateStr)
if err != nil {
panic("template occur error")
}
templ.Execute(os.Stdout,nil)
}
模板的复用 DRY (Don’t Repeat Yourself)
模板名称
每个模板都有个名称,当调用`template.PasreFiles()`时生成的每个模板名称默认为传入的文件名,当然我们也可以通过调用`template.New()`来指定模板的名称,模板的名称可以使得我们可以唯一标识生成的模板,通过`template.ExecuteTemplate()`我们可以执行指定名称的模板关联模板
为了重复利用代码,可以通过模板的名字,来指定调用某种模板或重复利用模板,我们可以在模板中定义内嵌模板,而在之后使用它,内嵌模板必须被定义在模板顶层// 模板的复用 Part 1
package main
import (
"os"
"text/template"
"fmt"
)
func main() {
templateStr := `
{{- define "TemplateA" -}}
Execute TemplateA
{{- end -}}
{{- define "TemplateB" -}}
Execute TemplateB
{{- end -}}
{{- define "TemplateMain" -}}
Execute TemplateMain
{{- print "\n" -}}
{{- template "TemplateA" -}}
{{- print "\n" -}}
{{- template "TemplateB" -}}
{{- end -}}
{{template "TemplateMain" -}}
`
templ := template.Must(template.New("main").Parse(templateStr))
//templ.Execute(os.Stdout,nil)
// 观察指定不同名字模板的输出
templ.ExecuteTemplate(os.Stdout,"main",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateMain",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateA",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateB",nil)
}
上述代码还可以用`block Action`改写
// 模板复用 Part 2
package main
import (
"os"
"fmt"
"text/template"
)
func main() {
templateStr := `
{{- block "TemplateMain" . -}}
Exectute TemplateMain
{{- print "\n" -}}
{{- block "TemplateA" . -}}
Execute TemplateA
{{- end -}}
{{- print "\n" -}}
{{- block "TemplateB" . -}}
Execute TemplateB
{{- end -}}
{{- end -}}
`
templ := template.Must(template.New("main").Parse(templateStr))
//templ.Execute(os.Stdout,nil)
templ.ExecuteTemplate(os.Stdout,"main",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateMain",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateA",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateB",nil)
}
// 模板复用 Part 3
package main
import (
"os"
"fmt"
"text/template"
)
func main() {
templateStr := `
{{- block "TemplateMain" . -}}
Exectute TemplateMain
{{- print "\n" -}}
{{- block "TemplateA" . -}}
Execute TemplateA
{{- end -}}
{{- print "\n" -}}
{{- block "TemplateB" . -}}
Execute TemplateB
{{- end -}}
{{- end -}}
`
newTemplateAStr := `
{{- define "TemplateA" -}}
Execute new TemplateA
{{- end -}}
`
newMainTemplateStr := `
{{- define "main" -}}
Execute New main Template
{{- end -}}
`
templ := template.Must(template.New("main").Parse(templateStr))
templ = template.Must(templ.Parse(newTemplateAStr))
templ = template.Must(templ.Parse(newMainTemplateStr))
//templ.Execute(os.Stdout,nil)
templ.ExecuteTemplate(os.Stdout,"main",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateMain",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateA",nil)
fmt.Println()
templ.ExecuteTemplate(os.Stdout,"TemplateB",nil)
}