在Go語言中,我們可以使用html/template標準庫將多個html文件組合起來。大家可能會問,爲什麼要組合多個模板呢?下面通過舉例來解答解答這個問題,順便學習一下template語法。
1. 字段操作
這裏的字段操作是指我們可以將自定義的內容替換模板文件的內容,當然這裏不限文件類型,txt文件也是可以進行替換的,只不過html/template會有一些針對html模板非常實用的方法。
{{ . }}
在上面的語法中使用雙括號括起來了一個點,點的兩邊有空格,這是模板語法必須要滿足的格式,其中的點是可以自己編輯的內容,在這裏相當於html中的this,表示傳入模板文件的對象,這樣說可能不好理解,下面舉個例子:
package main
import (
"html/template"
"os"
)
// 定義模板文件
var index = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div>
<p>This{{ . }}</p>
<p>Name:{{ .Name }}</p>
<p>Age :{{ .Age }}</p>
<p>Sex:{{ .Sex }}</p>
</div>
</body>
</html>
`
type Person struct {
Name string
Age int
Sex string
}
func main() {
p := Person{
Name: "random_w",
Age: 18,
Sex: "man",
}
tpl, err := template.New("Person").Parse(index)
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, p); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div>
<p>This{random_w 18 man}</p>
<p>Name:random_w</p>
<p>Age :18</p>
<p>Sex:man</p>
</div>
</body>
</html>
在Output中我們可以看到,點被替換成了我們在代碼中定義的p對象,裏面的值也被替換爲我們定義的值了,".Name"被替換爲random_w,“.Age”被替換爲18,“.Sex”被替換爲man。因此在點後面加元素名稱,相當於調用go語言中結構體的某一個元素值。
2. 遍歷({{ range … }}…{{ end }}和{{ range … }}…{{ else }}…{{ end }})
(1) {{ range … }}…{{ end }}
range-end語句類似於go語言中的for循環,他會遍歷後面的內容,語法如下:
{{ range array }}
{{ . }}
{{ end }}
第二種語法,我們可以通過變量獲取array的index及index對應的element:
{{range $index, $element := array}}
{{ $index }}
{{ $element }}
{{ end }}
如下面的例子:
package main
import (
"html/template"
"os"
)
// 定義模板文件
var index = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div>
<p>{{ range . }}<h3>{{ . }}<h3>{{ end }}</p>
<p>{{ range $i, $v := . }}<h3>{{ $i }}:{{ $v }}<h3>{{ end }}</p>
</div>
</body>
</html>
`
func main() {
Title := []string{"Title1", "Title2", "Title3"}
tpl, err := template.New("Person").Parse(index)
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, Title); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div>
<p><h3>Title1<h3><h3>Title2<h3><h3>Title3<h3></p>
<p><h3>0:Title1<h3><h3>1:Title2<h3><h3>2:Title3<h3></p>
</div>
</body>
</html>
(2) {{ range … }}…{{ else }}…{{ end }}
range-else-end這種語法類似於if-else,當array的長度爲0時執行else後面的內容。
package main
import (
"html/template"
"os"
)
// 定義模板文件
var index = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div>
<p>{{ range . }}<h3>{{ . }}<h3>{{ else }}Hello World!{{ end }}</p>
</div>
</body>
</html>
`
type Person struct {
Name string
Age int
Sex string
}
func main() {
var Title []string
tpl, err := template.New("Person").Parse(index)
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, Title); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<div>
<p>Hello World!</p>
</div>
</body>
</html>
和一看到Title爲空時,執行了else後面的操作。
3. 條件判斷
(1){{ if … }}…{{ end }}
如果if後面的值爲空(空字符、0、false、nil指針,nil接口, map或slice或array長度爲0),則無輸出。
package main
import (
"html/template"
"os"
)
func main() {
tpl, err := template.New("test").Parse("{{ if . }}OK{{ end }}")
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
上面的代碼輸出爲空。
(2){{ if … }}…{{ else }}…{{ end }}
如果if後面的值爲空(空字符、0、false、nil指針,nil接口, map或slice或array長度爲0),則執行else後面的語句。
package main
import (
"html/template"
"os"
)
func main() {
tpl, err := template.New("test").Parse("{{ if . }}OK{{ else }}False{{ end }}")
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
False
代碼輸出了else後面的值。
(3){{ and }}
返回bool值,相當於邏輯計算中的與,例如 and x y
package main
import (
"html/template"
"os"
)
func main() {
tpl, err := template.New("test").Parse("{{ and false true }}")
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
false
注意:and後面接的兩個值爲bool類型。
(4){{ or }}
返回bool值,相當於邏輯計算中的或,例如 or x y
package main
import (
"html/template"
"os"
)
func main() {
tpl, err := template.New("test").Parse("{{ or false true }}")
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
true
注意:or後面接的兩個值爲bool類型。
(5){{ with … }}…{{end}}和 {{ with … }}…{{ else }}…{{end}}
當with後面的值爲空(空字符、0、false、nil指針,nil接口, map或slice或array長度爲0),如果有else則輸出else後面的值,否則無輸出。
package main
import (
"html/template"
"os"
)
func main() {
tpl, err := template.New("test").Parse("{{ with . }}OK{{ else }}False{{ end }}")
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
False
4. 變量
可以定義的變量類型和go一樣,包括布爾型、字符串型、字符、整數、浮點型、虛數或負數,表現和Go的無類型常量一樣,nil和Go的無類型nil一樣。
(1)變量名前面使用"$"符
(2)struct的數據根據域(字段)訪問,支持鏈式訪問。
(3)map數據根據key來訪問,支持鏈式訪問。
(4)爲方法的調用,例如.Method結果是以調用方法的值使用“.”作爲接收器,dot.Method(). Mehod必須有一個或2個放回值
第二個返回值爲error類別,如果error不爲空,執行中斷,錯誤返回作爲執行結果。
方法調用支持鏈式操作:
.Field1.Key1.Method1.Field2.Key2.Method2
變量方式:
$x.Method1.Field
(5)函數
函數調用,直接使用函數名稱即可:
舉個例子:
package main
import (
"html/template"
"os"
"strings"
)
func main() {
// 首先創建一個函數字典用於註冊函數
funcMap := template.FuncMap{
// 註冊函數title, strings.Title會將單詞首字母大寫
"title": strings.Title,
}
tpl, err := template.New("test").Funcs(funcMap).Parse(`Output: {{ printf "%s" . | title }}`)
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, "hello world"); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
Output: Hello World
可以看到title方法將hello world的首字母大寫了。
5.內置函數
在執行期間,函數在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 |
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 |
6. 模板嵌套
(1){{ define . }}…{{ end }}
使用 {{ define “tplName” }} 定義模板名。
注意:define後面的模板名稱需要用雙引號引用,並且模板結尾已{{ end }}結尾。
(2){{ template . }}
通過{{ template “tplName” . }}引入其他模板。
注意:template後面的模板名稱需要用雙引號引用,模板名稱後面的點表示傳入數據,和字段操作是一個意思。
下面我們在項目的templates目錄創建一個header.html文件,使用template引用body模塊,內容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>html/template教程</title>
</head>
{{ template "body" . }}
</html>
我們在項目的templates目錄創建一個body.html文件,文件中使用define定義body模版,內容如下:
{{ define "body" }}
<body>
<p>Hello World</p>
</body>
{{ end }}
下面我們通過組合模板:
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
//導入模板
tpl, err := template.ParseFiles("templates/header.html", "templates/body.html")
if err != nil {
panic(err)
}
fmt.Println(tpl.Name())
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
header.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>html/template教程</title>
</head>
<body>
<p>Hello World</p>
</body>
</html>
可以看到我們成功將兩個模板組合起來。
7. 註釋
註釋語法很簡單類似於C語言,在雙括號中使用/*
和*/
括起來的語句就是註釋語句
package main
import (
"html/template"
"os"
)
func main() {
Title := []string{"Title1", "Title2", "Title3"}
// 注意註釋和兩個括號之間沒有空格
tpl, err := template.New("Person").Parse("{{/* 這裏是註釋 */}}{{ . }}")
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, Title); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
[Title1 Title2 Title3]
可以看到註釋內容並沒有被輸出。
8. pipeline
一個pipeline可能是鏈式的命令序列。一個命令可以是一個參數值、或一次函數調用、或一次方法調用。方法調用和函數調用可能有多個參數。
通過用管道字符“|”分隔一系列命令,可以“鏈接”管道。 在鏈式管道中,每個命令的結果將作爲以下命令的最後一個參數傳遞。 管道中最終命令的輸出是管道的值。
命令的輸出將是一個值或兩個值,第二個值具有類型錯誤。 如果存在第二個值並且計算結果爲非nil,則執行終止,並將錯誤返回給Execute的調用者。
下面舉個例子:
package main
import (
"html/template"
"os"
)
func main() {
tpl, err := template.New("test").Parse(`{{ with $x := "output" | printf "%s" }}{{$x}}{{ end }}`)
if err != nil {
panic(err)
}
// 如果不報錯則將內容輸出,os.Stdout爲標準輸出
if err := tpl.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
Output:
random@random-wz MINGW64 /d/GOCODE/Test
$ go run main.go
output
可以看到“|”將output傳遞給printf。