text/template实现基于数据的文本化输出。功能和jsp、blade.php(laravel)一样,用于动态生成文件,只不过golang用于生产HTML的模块为html/template1
运行过程
将template模板应用于结构化的数据,使用注解语法引用数据结构中的元素(struct中的feild或map中的key)并显示它们的值。template在执行过程中遍历数据结构并且设置当前光标(英文句号“.”标识)标识当前位置的元素(值)
一旦解析,模板可以安全地并行执行,但是如果并行执行共享Writer,则输出可以是交错的。
template的文本必须为UTF-8编码。注解语法必须由“{{”和“}}”分隔,不在“{{”和“}}”的文本原样输出。一般来说注解语法不用另起一行,可以和非注解文本同行,不过如果是动态注释的话,注解语法还是建议另起一行。
遍历struct
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) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
上面的例子中模板test变量sweaters中的Count和Material域,“.Count”中的“.”标识sweaters根元素
执行结果
➜ template git:(master) ✗ go run text_template.go
17 items are made of wool%
text
和spaces
虽然所有在“{{}}”控制注解之外的文本会默认一分不差地复制到输出中,但是如果“{{”紧接着跟随“—”和“ ”的话,那么“{{”之前的文本中的空白(空格、换行符、回车符、制表符)会被移除。对应的,“ -}}”表示移除之后文本中的空白。
例如上面例子,稍作修改:
package main
import (
"os"
"text/template"
)
type Inventory struct {
Material string
Count uint
}
func main(){
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count -}} items are made of {{- .Material}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
}
执行结果:
➜ template git:(master) ✗ go run text_template.go
17items are made ofwool%
Actions
以下pipeline是对数据的假设
{{/* 注释 */}}
{{- /* 去掉前后空白 */ -}}
//action的注释不会被输出,且必须在“{{}}”内,由“/*”和“*/”包含
{{pipeline}}
//pipeline值的文本化表示被复制到输出
if
{{if pipeline}} T1 {{end}}
如果pipeline为空(空字符、0、false、nil指针,nil接口, map或slice或array长度为0),无输出
if … else …
{{if pipeline}} T1 {{else}} T0 {{end}}
if…elseif …
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
{{range pipeline}} T1 {{end}}
pipeline
的必须为map、array、slice或channel。如果pipeline长度为0,无输出。如果长度大于0,则T1执行,“.”被设置为当前成功遍历的元素。如果pipeline为map并且key是可比较的,那么元素按照排序后的key值被访问
{{range pipeline}} T1 {{else}} T0 {{end}}
pipeline
的必须为map、array、slice或channel。如果pipeline长度为0, 执行T0。否则执行T1。
{{template "name"}}
执行指定模板name, 无任何数据传入
{{template "name" pipeline}}
执行指定模板name, pipeline数据传入
{{block "name" pipeline}} T1 {{end}}
定义模板name并执行模板,传入数据pipeline
{{with pipeline}} T1 {{end}}
如果pipeline不为空,那么执行T1, 并将光标“.” 设置为pipeline
{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline不为空,那么执行T1, 并将光标“.” 设置为pipeline;否则,执行T0, 光标“.”不受影响
布尔型、字符串型、字符、整数、浮点型、虚数或负数和在go语法一样不变,表现和Go的无类型常量一样
nil和Go的无类型nil一样
“.”代表当前光标的值
变量名前面使用"$"符,例如
$piOver2
struct的数据根据域(字段)访问,例如
.Field
支持链式调用:
.Field1.Field2
数据域可以保持在变量中,所以同时支持下面写法:
$x.Field1.Field2
map数据根据key来访问,例如:
.Key
支持链式访问:
.Field1.Key1.Field2.Key2
字典keys同样支持变量存储后访问:
$x.key1.key2
为方法的调用,例如
.Method
结果是以调用方法的值使用“.”作为接收器,dot.Method(). Mehod必须有一个或2个放回值
第二个返回值为error类别,如果error不为空,执行中断,错误返回作为执行结果。方法调用支持链式操作:
.Field1.Key1.Method1.Field2.Key2.Method2
变量方式:
$x.Method1.Field
函数调用:
fun
组合调用:
print (.F1 arg1) (.F2 arg2)
(.StructValuedMethod “arg”).Field
一个pipeline可能是链式的命令序列。一个命令可以是一个参数值、或一次函数调用、或一次方法调用。方法调用和函数调用可能有多个参数。
Argument
结果就是参数的值
.Method [Argument...]
method可以是单独的或者是链式调用的最后一个而不是链式调用的中间元素,这样才能获取参数
结果就是使用参数调用method:
dot.Method(Argument1, etc.)
functionName [Argument...]
使用参数调用方法:
function(Argument1, etc.)
通过用管道字符“|”分隔一系列命令,可以“链接”管道。 在链式管道中,每个命令的结果将作为以下命令的最后一个参数传递。 管道中最终命令的输出是管道的值。
命令的输出将是一个值或两个值,第二个值具有类型错误。 如果存在第二个值并且计算结果为非nil,则执行终止,并将错误返回给Execute的调用者。
Variables
在一次action内,pipeline可以使用变量捕获结果
$variable := pipeline
如果在一个range action内使用变量,可以给成功迭代的元素设置变量:
range $index,$element := range pipeline
变量的作用范围在它生命的控制结构内,直到“end”action结束.如果不在控制结构内生命的变量,直到template结束。
当template开始执行时,$变量被默认设置成传递个Execute函数的数据参数,也就是“.”光标的开始值
Pilelines和Variables例子
{{"\"output\""}}
字符串常量
{{`"output"`}}
字符原串常量
{{printf "%q" "output"}}
一次函数调用
{{"output" | printf "%q"}}
函数调用,最终的参数来自上一次的命令
{{printf "%q" (print "out" "put")}}
括号内的参数
{{"put" | printf "%s%s" "out" | printf "%q"}}
一个更精细的调用
{{"output" | printf "%s" | printf "%q"}}
链式调用.
{{with "output"}}{{printf "%q" .}}{{end}}
使用with action的调用,.为pipeline的值
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
with action 创建变量和使用变量
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
with action 创建变量并在作用范围内在另一action使用变量
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
with action 创建变量和使用管道“|”
在执行期间,函数在2个函数字典查找:首先查找template函数字典,然后查找全局函数字典。默认情况下,template函数字典没有函数,不过提供了Funcs方法设置template函数字典。
预定义的全局函数有:
and 返回bool值,例如 and x y
call 第一个参数为调用的函数,其他为该函数的参数,例如 call .X.Y 1 2,
等效dot.X.Y(1, 2) ,该函数一样必须由1个或2个返回值,第二个为error类型
html 返回参数的文本化表示的HTML。 此功能不可用
在html / template中,有一些异常。
index 例如, index x 1 2 3 。 表示x[1][2][3]
js 返回参数的文本化表示的javascript
len 返回参数的长度
not 返回参数的否定值
or 或运算,例如 or x y
print
fmt.Sprint别名
printf
fmt.Sprintf别名
println
fmt.Sprintln别名
urlquery
返回文本表示形式的转义值, 它的参数以适合嵌入URL查询的形式出现。
上面的bool函数把任何零值当做false,非零值当做true
比较函数:
符号 | 含义 | 内容 | |
---|---|---|---|
eq | 等于 | 操作符 | arg1 == arg2 |
ne | 不等于 | 操作符 | arg1 != arg2 |
lt | 小于 | 操作符 | arg1 < arg2 |
le | 小于等于 | 操作符 | arg1 <= arg2 |
gt | 大于 | 操作符 | arg1 > arg2 |
ge | 大于等于 | 操作符: | arg1 >= arg2 |
package main
import (
"log"
"os"
"strings"
"text/template"
)
func main() {
// 首先创建一个函数字典用于注册函数
funcMap := template.FuncMap{
// 注册函数title, strings.Title会将单词首字母大写
"title": strings.Title,
}
// A simple template definition to test our function.
// 打印输出文本
// - 原样
// - 执行title后的
// - 执行title和printf
// - 执行printf和title
const templateText = `
Input: {{printf "%q" .}}
Output 0: {{title .}}
Output 1: {{title . | printf "%q"}}
Output 2: {{printf "%q" . | title}}
`
// 创建模板, 添加模板函数,添加解析模板文本.
tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText)
if err != nil {
log.Fatalf("parsing: %s", err)
}
// 运行模板,出入数据参数
err = tmpl.Execute(os.Stdout, "the go programming language")
if err != nil {
log.Fatalf("execution: %s", err)
}
}
运行结果:
➜ template git:(master) ✗ go run text_f2.go
Input: “the go programming language”
Output 0: The Go Programming Language
Output 1: “The Go Programming Language”
Output 2: “The Go Programming Language”
package main
import (
"log"
"os"
"strings"
"text/template"
)
func main() {
const (
master = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}`
overlay = `{{define "list"}} {{join . ", "}}{{end}} `
)
var (
funcs = template.FuncMap{"join": strings.Join}
guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"}
)
masterTmpl, err := template.New("master").Funcs(funcs).Parse(master)
if err != nil {
log.Fatal(err)
}
overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay)
if err != nil {
log.Fatal(err)
}
if err := masterTmpl.Execute(os.Stdout, guardians); err != nil {
log.Fatal(err)
}
if err := overlayTmpl.Execute(os.Stdout, guardians); err != nil {
log.Fatal(err)
}
}