如何在 Golang 中使用模板?
标准模板包的概述
在本文中,我将解释 Go 语言(Golang)的标准模板包的基础知识。这些基础知识包括在 Golang 模板中使用变量、条件语句、遍历变量以及将函数应用于变量。
Golang 提供了 text/template
和 html/template
包,以便直接处理模板。
第一个包是最通用的一个——你可以用它来创建所有种类的文本字符串的模板。第二个包更具针对性地用于 HTML——它在处理 HTML 网页环境中的不安全变量时非常方便。
这些包含有各种可以加载、解析和评估模板文本或(HTML 或文本)文件的函数。
例如,你可以使用以下函数:
- 使用
Parse
解析程序内部存在的文本字符串。 - 使用
ParseFiles
加载和解析模板文件。 - 使用
Execute
通过特定的数据字段将模板渲染到某些输出。
接下来,我将讨论在 Golang 中创建强大模板的基本构建模块。
外部(程序)变量
你可以从实际的 Go 程序发送变量到你的模板。然后你可以在模板中使用这些变量。
首先,当你希望在模板中渲染特定的操作时,你可以通过在文本字符串中添加双括号 {{}}
来实现这一点。
我们可以利用这个特性来显示你从程序中提供的变量。所以,如果你在双括号中添加一个点,所有的数据字段都将在那里被渲染。
例如,{{.}}
将把你所有的数据字段渲染为一个格式化的字符串。
此外,可以通过指定该字段的名称来访问你的数据的特定字段。
例如,{{ .Title }}
只会将 Title
字段渲染为一个格式化的字符串。
你可以在下面的代码示例中看到这些操作的应用。
package main
import (
"os"
"text/template"
)
type Book struct {
Title string
Publisher string
Year int
}
func main() {
t1 := template.New("Template")
t1, _ = t1.Parse("External variable has the value [{{.}}]\n")
t1.Execute(os.Stdout, "Amazing")
b := Book{"The CSound Book", "MIT Press", 2002}
t1.Execute(os.Stdout, b)
t2 := template.New("Template")
t2, _ = t2.Parse("External variable Book has the values [Title: {{.Title}}, Publisher: {{.Publisher}}, Year: {{.Year}}]\n")
t2.Execute(os.Stdout, b)
}
// Output
// External variable has the value [Amazing]
// External variable has the value [{The CSound Book MIT Press 2002}]
// External variable Book has the values [Title: The CSound Book, Publisher: MIT Press, Year: 2002]
在上述代码中,我们首先使用 template.New()
创建一个新的空模板。然后我们使用 Parse()
将一个字符串解析到这个模板中。在这个字符串中,我们在双括号间加入了一个动作。
因为我们在括号之间放了一个点,这向模板渲染器发出了一个信号,即需要在这里渲染提供的变量。
使用 Execute()
来渲染带有变量的模板。第一个参数 (os.Stdout
) 是渲染的模板要输出的位置。第二个参数 (Amazing
) 是我们希望在模板中渲染的变量。在这种情况下,它只是一个字符串。
我们可以为模板提供的变量,比如说,一个结构体,而不仅仅是一个字符串。在这里,我们创建了一个用于保存书籍数据的结构体。然后,我们可以将其提供给我们之前创建的相同模板。
也可以渲染结构体的单独数据字段。为此,创建一个新的模板字符串。在双括号之间,我们放一个点,后面跟着我们希望渲染的结构体数据字段的名称,而不仅仅是一个点。请注意,这个数据字段是以大写字母写的,表示它是可导出的。
注意 Parse
函数实际上返回两个变量。第一个变量是解析后的模板,第二个是错误消息。在上述示例中,我们不会使用错误消息,所以我们只放一个下划线。
内部(模板)变量
你可以定义内部变量,即只在模板内定义的变量。
下面的模式展示了如何在模板字符串中定义并使用一个变量。首先是定义,然后是使用。内部变量应该以美元符号 ($) 开头。
{{$var:=`value`}} ... {{$var}} ...
下面是一个简单的例子:
package main
import (
"os"
"text/template"
)
func main() {
t, _ := template.New("Template").Parse("{{$var:=2150}}Internal variable has the value [{{$var}}]")
t.Execute(os.Stdout, nil)
}
// Output:
// Internal variable has the value [2150]
与使用点渲染外部变量不同,在这里我们提到了我们之前在双括号之间定义的变量。
条件语句
以下是条件语句的一般模式:
{{if [..]}} if-part {{end}}
{{if [..]}} if-part {{else}} else-part {{end}}
{{if [..]}} if-part {{if else}} if-else-part {{end}}
在最后的模式中,你可以根据需要多次使用 {{if else}}
部分,也可以添加一个 {{else}}
部分。以下是第二种模式的一个简单示例:
package main
import (
"os"
"text/template"
)
func main() {
t, err := template.New("Template").Parse("{{if eq . `filler`}}This is filler...{{else}}It's something else...{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, "filler")
}
// Output:
// This is filler...
注意,在上面的例子中,我们也使用了从 Parse()
函数返回的错误消息。如果没有错误,错误消息等于 'nil'。如果有错误,我们可以使用 panic()
函数来处理。
另外,你可以使用以下操作符,而不只是等于 eq
(对应 ==):
- 非等于
ne
(对应 !=) - 小于
lt
(对应 <) - 小于等于
le
(对应 <=) - 大于
gt
(对应 >) - 大于等于
ge
(对应 >=)
此外,你还可以直接写入布尔变量,而不是比较语句。
package main
import (
"os"
"text/template"
)
func main() {
t, _ := template.New("Template").Parse("{{if .}}This is true.{{else}}This is false.{{end}}\n")
t.Execute(os.Stdout, false)
}
// Output:
// This is false
循环
在遍历数组、切片或映射时,你可以在模板中使用几种模式。首先,我们来看看最简单的形式。
{{range .Var}}
{{.}}
{{end}}
在这里,我们遍历数组、切片或映射(.Var)中的每个变量。在循环的每一步中,只有一个变量可供在循环中使用。
现在,{{.}}
不再代表我们模板可用的所有变量,而只代表在循环中可用的那个变量。
package main
import (
"os"
"text/template"
)
func main() {
computerList := []string{"Arduino", "Raspberri Pi", "NVidia Jetson Nano"}
t, err := template.New("Template").Parse("My favorite computers are:\n{{range .}}{{.}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, computerList)
}
// Output:
// My favorite computers are:
// Arduino
// Raspberri Pi
// NVidia Jetson Nano
当你在一堆变量中进行循环时,可以使用以下更复杂的模式:
{{range $index, $element := . }}
{{$index}} ... {{$element}} ...
{{end}}
这个模式在你需要在循环中使用变量索引时非常方便。例如,如果你希望输出一个有序的带编号的列表,你可以使用这个模板模式。
注意,这里的变量 $index
和 $element
是在模板内部定义的内部变量。因此,它们也以 ''$' 符号开头。
package main
import (
"os"
"text/template"
)
func main() {
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
t, err := template.New("Template").Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{$index}}) {{$item}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, dishesList)
}
// Output
// My favorite dishes are:
// 0) Enciladas con Pollo
// 1) Hot&Spicy Pizza
// 2) Spaghetti Bolognese
如你所见,这种方法有一个问题。我们从输出中得到的有序列表从0开始。原因是第一个索引总是0。
对于许多应用来说,这并不是我们想要的。我们希望有序列表从1开始。那么,我们如何让每个索引增加1呢?我们需要一个函数来增加给定的变量。
在下一部分,我们将看到如何在模板字符串中运行函数。
在模板中使用函数
在模板中可以使用函数。
为此,你需要将你希望在模板中使用的函数映射到一个关键字。
在下面的例子中,我们将创建一个叫做 add()
的添加函数。我们将使用 template.FuncMap{}
在 FuncMap()
函数内将其添加到函数映射中。
有了 add
函数,我们将能够向 $index
变量添加1,从而增加它。结果将是一个更易读的有序列表。
package main
import (
"os"
"text/template"
)
func add(a, b int) int {
return a + b
}
func main() {
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
t, err := template.New("Template").Funcs(template.FuncMap{"add": add}).Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{add $index 1}}) {{$item}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, dishesList)
}
// Output:
// My favorite dishes are:
// 1) Enciladas con Pollo
// 2) Hot&Spicy Pizza
// 3) Spaghetti Bolognese
你可以在下面找到一个更复杂的例子。这里我们添加了第二个函数。想法是创建一个CSV风格的输出,但当我们在函数映射中添加“分隔符”时,我们可以选择我们想要的分隔符。
你也可以看到,映射中使用的名称并不是实际函数名称。
package main
import (
"os"
"text/template"
)
func add(a, b int) int {
return a + b
}
func delimiter(s string) func() string {
return func() string {
return s
}
}
func main() {
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
tmpl := "Index{{dl}}Dish\n{{range $index, $item:=.}}{{add $index 1}}{{dl}}{{$item}}\n{{end}}\n"
funcMap := template.FuncMap{"add": add, "dl": delimiter(",")}
t, _ := template.New("Template").Funcs(funcMap).Parse(tmpl)
t.Execute(os.Stdout, dishesList)
}
// Output:
// Index,Dish
// 1,Enciladas con Pollo
// 2,Hot&Spicy Pizza
// 3,Spaghetti Bolognese
有一个方便的库,叫做Sprig,它有模板函数映射。你可以在这里找到它。然而,这个库似乎只能和html/template
包一起使用。
列表清单
请记住,始终保持学习的态度,并享受编码的乐趣!祝您编码愉快!
如果你喜欢我的文章,点赞,关注,转发!