go标准库之template

214 阅读5分钟

go标准库之template

templates包定义了数据驱动的文本输出。生成html文件的模板在html/template包下面。模板使用插值语法{{.var}}格式,也可以使用一些流程控制,例如判断if else、循环range还可以使用一些函数,包括内建函数和自定义函数。

第一个模板实例

func test1() {
	name := "zs"
	// {{.}} 表示占位一个变量
	templateStr := "Hello,{{.}}"
        // test为模板名称
	t := template.New("test")
	t2, err := t.Parse(templateStr)
	if err != nil {
		log.Fatal(err)
	}

	t2.Execute(os.Stdout, name) // Hello,zs
}

也可以是结构体

package main
import (
    "os"
    "text/template"
)
type Person struct {
    Name string
    Age  int
}
func main() {
    ghz := Person{"ghz", 80}
    muban := "hello, {{.Name}}, Your age {{.Age}}"
    tmpl, err := template.New("test").Parse(muban)
    if err != nil {
        panic(err)
    }
    err = tmpl.Execute(os.Stdout, ghz)
    if err != nil {
        panic(err)
    }
}

运行结果

hello, ghz, Your age 80

html模板

定义一个HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test golang template</title>
</head>
<body>
   {{.}}
</body>
</html>

定义一个HttpServer

package main
import (
    "html/template"
    "net/http"
)
func tmpl(w http.ResponseWriter, r *http.Request) {
    t1, err := template.ParseFiles("test.html")
    if err != nil {
        panic(err)
    }
    t1.Execute(w, "hello world")
}
func main() {
    server := http.Server{
        Addr: "127.0.0.1:8080",
    }
    http.HandleFunc("/tmpl", tmpl)
    server.ListenAndServe()
}

客户端访问

http://localhost:8080/tmpl

运行结果

hello,ghz

文本和空格

模板引擎在进行替换的时候,是完全按照文本格式进行替换的。除了需要评估和替换的地方,所有的行分隔符、空格等等空白都原样保留。所以,对于要解析的内容,不要随意缩进、随意换行

例如:

{{23}} < {{45}}        -> 23 < 45
{{23}} < {{- 45}}      ->  23 <45
{{23 -}} < {{45}}      ->  23< 45
{{23 -}} < {{- 45}}    ->  23<45  // 把23后面的空格与35左边的空格去掉

去掉后空格 xxxx -}},去掉前空格{{- xxxx

模板注释

注释方式:{{/* a comment */}}

注释后的内容不会被引擎进行替换。但需要注意,注释行在替换的时候也会占用行,所以应该去除前缀和后缀空白,否则会多一空行。

{{- /* a comment without prefix/suffix space */}}
{{/* a comment without prefix/suffix space */ -}}
{{- /* a comment without prefix/suffix space */ -}}

管道pipeline

管道就是一系列命令的链式调用。当然,也可以是一个命令,例如:计算表达式的值{{.}}{{.Name}},或者是一个函数调用或者方法调用。

可以使用管道符号|链接多个命令,用法和unix下的管道类似:|前面的命令将运算结果(或返回值)传递给后一个命令的最后一个位置。

需要注意的是,并非只有使用了|才是pipeline。Go template中,pipeline的概念是传递数据,只要能产生数据的,都是pipeline。

下面是Pipeline的几种示例,它们都输出"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"}}

可以在HTML中测试

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test golang template</title>
</head>
<body>
    {{`"output"`}} <br>
    {{printf "%q" "output"}}<br>
    {{"output" | printf "%q"}}<br>
    {{printf "%q" (print "out" "put")}}<br>
    {{"put" | printf "%s%s" "out" | printf "%q"}}<br>
    {{"output" | printf "%s" | printf "%q"}}<br>
</body>
</html>

运行结果

"output"
"output"
"output"
"output"
"output"
"output"

变量

变量的语法

// 未定义过的变量
$var := pipeline
// 已定义过的变量
$var = pipeline

例如

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test golang template</title>
</head>
<body>
    {{$Name := "tom"}}
    {{$Name = "kite"}}
    {{$Name}}<br>
    {{$len := (len "hello,ghz")}}
    {{$len}}
</body>
</html>

运行结果

kite
9

条件判断

语法

{{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}}

pipeline为false的情况是各种数据对象的0值:数值0,指针或接口是nil,数组、slice、map或string则是len为0。

可以使用如下运算符表达式

eq
    Returns the boolean truth of arg1 == arg2
ne
    Returns the boolean truth of arg1 != arg2
lt
    Returns the boolean truth of arg1 < arg2
le
    Returns the boolean truth of arg1 <= arg2
gt
    Returns the boolean truth of arg1 > arg2
ge
    Returns the boolean truth of arg1 >= arg2

实例

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test golang template</title>
</head>
<body>
    {{$Age := 18}}
    {{if (ge $Age 18)}}
    <h3>你已经成年!</h3>
    {{else}}
    <h3>你还未成年!</h3>
    {{end}}
    
</body>
</html>

运行结果

你已经成年!

循环迭代

语法

{{range pipeline}} T1 {{end}}
{{range pipeline}} T1 {{else}} T0 {{end}}

range可以迭代slice、数组、map或channel。迭代的时候,会设置"."为当前正在迭代的元素。对于第一个表达式,当迭代对象的值为0值时,则range直接跳过,就像if一样。对于第二个表达式,则在迭代到0值时执行else语句。

实例演示

go代码

func tmpl(w http.ResponseWriter, r *http.Request) {
    t1, err := template.ParseFiles("test.html")
    if err != nil {
        panic(err)
    }
    s := []string{"多课网", "golang 教程", "老郭"}
    t1.Execute(w, s)
}

html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test golang template</title>
</head>
<body>
    {{range $x := . -}}
        {{println $x}}
    {{- end}}
</body>
</html>

运行结果

多课网 golang 教程 老郭

with...end

with用来设置"."的值,语法如下:

{{with pipeline}} T1 {{end}}
{{with pipeline}} T1 {{else}} T0 {{end}}

对于第一种格式,当pipeline不为0值的时候,点"."设置为pipeline运算的值,否则跳过。对于第二种格式,当pipeline为0值时,执行else语句块,否则"."设置为pipeline运算的值,并执行T1。

实例演示

{{with "多课网-golang-老郭"}}{{println .}}{{end}}

运行结果

多课网-golang-老郭

内置函数

and
    返回第
    一个空参数或最后一个参数返回其参数的布尔值 AND ,即
    “and x y”表现为“if x then y else x”。
    评估所有参数。
call
    返回调用第一个参数的结果,该参数
    必须是一个函数,其余参数作为参数。
    因此,“call .XY 1 2”在 Go 表示法中是 dot.XY(1, 2),其中
    Y 是函数值字段、映射条目等。
    第一个参数必须是
    产生函数类型值的评估结果(不同于
    预定义的函数,如打印)。该函数必须
    返回一个或两个结果值,其中第二个
    是类型错误。如果参数与函数不匹配
    或返回的错误值为非零,则执行停止。
html
    返回等效
    于其参数文本表示的转义 HTML 。此功能
    在 html/template 中不可用,但有一些例外。
index
    返回通过
    以下参数对其第一个参数进行索引的结果。因此,在 Go 语法中,“索引 x 1 2 3”是
    x[1][2][3]。每个索引项必须是映射、切片或数组。
slice 
    slice 返回其第一个参数被
    其余参数切片的结果。因此,"slice x 1 2" 在 Go 语法中是 x[1:2],
    而 "slice x" 是 x[:],"slice x 1" 是 x[1:],
    是 x[1:2:3]。第一个参数必须是字符串、切片或数组。
js
    返回转义的 JavaScript 等效
    于其参数的文本表示。
len
    返回其参数的整数长度。
not
    返回其单个参数的布尔否定。
or
    通过返回第
    一个非空参数或最后一个参数来返回其参数的布尔 OR ,即
    “or x y”表现为“if x then x else y”。
    评估所有参数。
print 
    fmt.Sprint
    的别名
printf fmt.Sprintf
    的别名
println fmt.Sprint的别名
urlquery
    以适合嵌入 URL 查询的形式
    返回其参数的文本表示的转义值。
    此功能在 html/template 中不可用,但有一些
    例外。

嵌套template:define和template

define可以直接在待解析内容中定义一个模板,这个模板会加入到common结构组中,并关联到关联名称上。

{{template "name"}}
{{template "name" pipeline}}
{{define "name"}}

实例演示

假设我们有一个header.html、footer.html和index.html,index.html包含header.html和footer.html

header.html

{{define "header"}}
     <head>
         <title>{{.Title}}</title>
     </head>
{{end}}

go code

package main
import (
    "html/template"
    "net/http"
)
func tmpl(w http.ResponseWriter, r *http.Request) {
    t1, err := template.ParseFiles("templates/index.html", "templates/header.html", "templates/footer.html")
    if err != nil {
        panic(err)
    }
    t1.Execute(w, nil)
}
func main() {
    server := http.Server{
        Addr: "127.0.0.1:8080",
    }
    http.HandleFunc("/tmpl", tmpl)
    server.ListenAndServe()
}

运行结果

这是header
首页...
这是footer