上面我們示範了如何解析並渲染範本,接下來讓我們來更加詳細的了解如何把資料渲染出來。一個範本都是應用在一個 Go 的物件之上,Go 物件的欄位如何插入到範本中呢?
欄位操作
Go 語言的範本透過 {{}} 來包含需要在渲染時被替換的欄位,{{.}}表示當前的物件,這和 Java 或者 C++中的 this 類似,如果要存取當前物件的欄位透過{{.FieldName}},但是需要注意一點:這個欄位必須是匯出的(欄位首字母必須是大寫的),否則在渲染的時候就會報錯,請看下面的這個例子:
package main
import (
"html/template"
"os"
)
type Person struct {
UserName string
}
func main() {
t := template.New("fieldname example")
t, _ = t.Parse("hello {{.UserName}}!")
p := Person{UserName: "Astaxie"}
t.Execute(os.Stdout, p)
}
範本在輸出物件的欄位值時,採用了 fmt 套件把物件轉化成了字串。但是有時候我們的需求可能不是這樣的,例如有時候我們為了防止垃圾郵件傳送者透過採集網頁的方式來發送給我們的郵箱資訊,我們希望把 @ 替換成 at 例如:astaxie at beego.me,如果要實現這樣的功能,我們就需要自訂函式來做這個功能。
每一個範本函式都有一個唯一值的名字,然後與一個 Go 函式關聯,透過如下的方式來關聯
type FuncMap map[string]interface{}
例如,如果我們想要的 email 函式的範本函式名是emailDeal,它關聯的 Go 函式名稱是EmailDealWith,那麼我們可以透過下面的方式來註冊這個函式
t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith})
EmailDealWith這個函式的參數和回傳值定義如下:
func EmailDealWith(args …interface{}) string
我們來看下面的實現例子:
package main
import (
"fmt"
"html/template"
"os"
"strings"
)
type Friend struct {
Fname string
}
type Person struct {
UserName string
Emails []string
Friends []*Friend
}
func EmailDealWith(args ...interface{}) string {
ok := false
var s string
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
s = fmt.Sprint(args...)
}
// find the @ symbol
substrs := strings.Split(s, "@")
if len(substrs) != 2 {
return s
}
// replace the @ by " at "
return (substrs[0] + " at " + substrs[1])
}
func main() {
f1 := Friend{Fname: "minux.ma"}
f2 := Friend{Fname: "xushiwei"}
t := template.New("fieldname example")
t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith})
t, _ = t.Parse(`hello {{.UserName}}!
{{range .Emails}}
an emails {{.|emailDeal}}
{{end}}
{{with .Friends}}
{{range .}}
my friend name is {{.Fname}}
{{end}}
{{end}}
`)
p := Person{UserName: "Astaxie",
Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"},
Friends: []*Friend{&f1, &f2}}
t.Execute(os.Stdout, p)
}
範本套件裡面有一個函式Must,它的作用是檢測範本是否正確,例如大括號是否匹配,註釋是否正確的關閉,變數是否正確的書寫。接下來我們示範一個例子,用 Must 來判斷範本是否正確:
package main
import (
"fmt"
"text/template"
)
func main() {
tOk := template.New("first")
template.Must(tOk.Parse(" some static text /* and a comment */"))
fmt.Println("The first one parsed OK.")
template.Must(template.New("second").Parse("some static text {{ .Name }}"))
fmt.Println("The second one parsed OK.")
fmt.Println("The next one ought to fail.")
tErr := template.New("check parse error with Must")
template.Must(tErr.Parse(" some static text {{ .Name }"))
}
將輸出如下內容
The first one parsed OK.
The second one parsed OK.
The next one ought to fail.
panic: template: check parse error with Must:1: unexpected "}" in command
巢狀範本
我們平常開發 Web 應用的時候,經常會遇到一些範本有些部分是固定不變的,然後可以抽取出來作為一個獨立的部分,例如一個部落格的頭部和尾部是不變的,而唯一改變的是中間的內容部分。所以我們可以定義成header、content、footer三個部分。Go 語言中透過如下的語法來宣告