Go 视图模板篇(二):通过指令实现控制结构和模板引入


指令用于在 Go 模板中嵌入命令,通过 {{}} 来定义,Go 提供了丰富的指令集,包括条件判断、循环、设置和引入等。

在众多 Go 模板指令中,. 是最重要的一个,它用于解析传递到模板的数据,其他指令和函数大多都是围绕这个 . 进行格式化和显示。

条件指令

要在视图模板中使用 if 条件判断,可以这么做:

{{ if arg }} 
    some content 
{{ end }}

还可以编写 if...else... 控制结构语句:

{{ if arg }} 
    some content 
{{ else }}
    other content 
{{ end }}

其中 arg 可以是常量、变量、或者返回某个值的函数或方法。

下面看一个简单的示例,编写服务端处理器代码如下:

package main
    
import (
    "html/template"
    "math/rand"
    "net/http"
    "time"
)
    
func process(w http.ResponseWriter, r *http.Request)  {
    t := template.Must(template.ParseFiles("condition.html"))
    rand.Seed(time.Now().Unix())
    t.Execute(w, rand.Intn(10) > 5)
}
    
func main()  {
    http.HandleFunc("/condition", process)
    http.ListenAndServe(":8080", nil)
}

对应的模板代码 condition.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Condition Actions</title>
</head>
<body>
    {{ if . }}
        Number is greater than 5!
    {{ else }}
        Number is less than or equal to 5!
    {{ end }}
</body>
</html>

运行服务端代码启动服务器,在终端窗口通过 curl 请求 /condition 路由,可以看到对应的返回结果如下:

-w513

迭代指令

迭代指令可以用于循环迭代数组、切片、字典和通道:

{{ range array }} 
    Dot is set to the element {{ . }} 
{{ end }}

编写一段服务端处理器示例代码如下:

package main
    
import (
    "html/template"
    "net/http"
)
    
func iterationActionExample( w http.ResponseWriter, r *http.Request)  {
    t := template.Must(template.ParseFiles("iteration.html"))
    daysOfWeek := []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
    t.Execute(w, daysOfWeek)
}
    
func main() {
    http.HandleFunc("/iteration", iterationActionExample)
    http.ListenAndServe(":8080", nil)
}

以及对应的模板代码 iteration.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Iteration Actions</title>
</head>
<body>
    <ul>
        {{ range . }}
            <li>{{ . }}</li>
        {{ end }}
    </ul>
</body>
</html>

运行服务端代码启动服务器,在浏览器访问 http://localhost:8080/iteration,输出结果如下:

-w773

可以看到无论是外层的循环体,还是循环体内部的元素,都是通过 . 来替代。如果待迭代的变量为空的话,还可以通过下面这种方式来处理:

<ul>
    {{ range . }}
        <li>{{ . }}</li>
    {{ else }}
        <p>Nothing to show</p>
    {{ end }}
</ul>

设置指令

此外,在 Go 模板中,还可以通过 with 指令设置变量值:

{{ with arg }} 
    Dot is set to arg 
{{ end }}

这样一来,在 {{ with arg }}{{ end }} 之间的 . 会被设置为 arg

我们编写一段示例代码进行演示,对应的服务端处理器代码如下:

package main
    
import (
    "html/template"
    "net/http"
)
    
func setActionExample(w http.ResponseWriter, r *http.Request)  {
    t := template.Must(template.ParseFiles("set.html"))
    t.Execute(w, "golang")
}
    
func main()  {
    http.HandleFunc("/set_action", setActionExample)
    http.ListenAndServe(":8080", nil)
}

对应的模板文件 set.html 代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Set Action</title>
</head>
<body>
    <div>The dot is {{ . }}</div>
    <div>
        {{ with "php" }}
            Now the dot is set to {{ . }}
        {{ end }}
    </div>
    <div>The dot is {{ . }} again</div>
</body>
</html>

运行服务端代码启动服务器,在浏览器中访问 http://localhost:8080/set_action,返回结果如下:

-w681

同样,设置指令也支持 else

{{ with arg }} 
    Dot is set to arg 
{{ else }}
    Fallback if arg is empty 
{{ end }}

其含义是如果 arg 值为空,则调用 else 区块对应的逻辑,例如:

{{ with "" }} 
    Dot is set to {{ . }} 
{{ else }}
    Dot is still {{ . }}
{{ end }}

引入指令

最后,我们还可以通过引入指令来嵌入子模板:

{{ template "name" }}

我们编写一段服务端处理器示例代码如下,这里我们解析了两个模板文件,其中 t1.html 是主模板,t2.html 是前者引入的子模板:

package main
    
import (
    "html/template"
    "net/http"
)
    
func includeActionExample(w http.ResponseWriter, r *http.Request)  {
    t := template.Must(template.ParseFiles("t1.html", "t2.html"))
    t.Execute(w, "Hello World!")
}
    
func main()  {
    http.HandleFunc("/include", includeActionExample)
    http.ListenAndServe(":8080", nil)
}

对应的模板文件 t1.html 代码(主模板,通过 template 指令引入子模板 t2.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Template 1</title>
</head>
<body>
    <div> This is t1.html before </div>
    <div> This is the value of the dot in t1.html - [{{ . }}] </div>
    <hr/>
    {{ template "t2.html" }}
    <hr/>
    <div> This is t1.html after </div>
</body>
</html>

以及模板文本 t2.html 代码(这是一个子模板):

<div style="background-color: yellow;">
    This is t2.html
    <br/>
    This is the value of the dot in t2.html - [{{ . }}]
</div>

运行服务端代码启动服务器,在浏览器中访问 http://localhost:8080/include,输出结果如下:

-w676

可以看到嵌套模板中的变量值为空,这是因为我们没有从第一个模板将变量传入第二个模板,如果要传入的话可以这么做:

{{ template "t2.html" . }}

这样就可以在嵌套模板中看到这个值了:

-w675


Vote Vote Cancel Collect Collect Cancel

<< 上一篇: Go 视图模板篇(一):模板引擎的定义、解析与执行

>> 下一篇: Go 视图模板篇(三):参数、管道和函数调用