Go語言中的html/template標準庫學習筆記二(template語法介紹)

在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
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

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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章