65.模板详细使用(一)

模板是构建丰富结构的web页面的主要手段。所以需要再深入了解一下。
首先构建一个handler

func templateHandler(writer http.ResponseWriter, request *http.Request) {
    t, err := template.ParseFiles("./JoelTempWeb/layout.html")

    if err != nil{
        fmt.Fprintln(writer,err)
    }
    fmt.Println("template name is ",t.Name())
    t.Execute(writer,"hello moon")

}

当然需要配套的html模板


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>layout</title>
</head>
<body>
<h3>This is layout</h3>
template data:{{ . }}

</body>
</html>

这里的 {{.}}就是显示func templateHandler中的给出的数据 "hello moon" 的。所以,当main里这样之后

http.HandleFunc("/layout/", templateHandler)
http.ListenAndServe(":8090", nil)

运行结果如下


hello moon的示例

完整main代码

package main

import (
    "fmt"
    "joeltest/JoelTempWeb"
)

func main()  {
    JoelTempWeb.JoelHttpPath()
    JoelTempWeb.JoelListenAndServe()
}

完整JoelTempWeb代码

package JoelTempWeb

import (
    "net/http"
    "time"
    "html/template"
    "fmt"
    "math/rand"
    "strconv"
    "math"
)

//Joel路径
func JoelHttpPath() {
    http.HandleFunc("/layout/", templateHandler)
}

//Joel监听
func JoelListenAndServe() {
    http.ListenAndServe(":8090", nil)
}
//=================================
func templateHandler(writer http.ResponseWriter, request *http.Request) {
    t, err := template.ParseFiles("./JoelTempWeb/layout.html")
    if err != nil{
        fmt.Fprintln(writer,err)
    }
    fmt.Println("template name is ",t.Name())
    t.Execute(writer,"hello moon")
}

//--------------------------------

模板页layout

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>layout</title>
</head>
<body>
<h3>This is layout</h3>
template data:{{ . }}

</body>
</html>

模板还是可以命名的,命名的方式是

t.ExecuteTemplate(writer, "layout", "Hello Moon!!!")

这样,这个模板的名字就是 layout 了。
那么在模板页面中,可以添加 define 来指定 action 的名字。
于是,在模板文件中,可以添加

{{define "layout"}}
{{end}}
内容是一样显示的

layout.html的代码变成了这样

{{define "layout"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>layout</title>
</head>
<body>
<h3>This is layout</h3>
template data:{{ . }}

</body>
</html>
{{end}}

在页面的结构中,可以引入多个模板。比如,我们在引入一个index.html

{{define "index"}}
<div style="background-color: chocolate;width: 555px">
    this is index.html
{{.}}
</div>
{{end}}

那么在 func templateHandler 中就要修改成这个样子

func templateHandler(writer http.ResponseWriter, request *http.Request) {
    t, err := template.ParseFiles("./JoelTempWeb/layout.html","./JoelTempWeb/index.html")
    if err != nil{
        fmt.Fprintln(writer,err)
    }
    fmt.Println("template name is ",t.Name())
    t.ExecuteTemplate(writer, "layout", "Hello Moon!!!")
}

因为 index.html 中 define "index" 了,所以,在 layout.html 中加入 index模板的时候就可以直接写 template "index"

{{define "layout"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>layout</title>
</head>
<body>
<h3>This is layout</h3>
template data:{{ . }}
{{template "index"}}
</body>
</html>
{{end}}
这个巧克力色的行,就是index模板添加进来的效果了

当然,如果你把 t.ExecuteTemplate(writer, "layout", "Hello Moon!!!")中的 layout 改为 index ,你就会发现,只剩下index模板的内容了。并且因为使用了index的名字,所以index模板中的 {{.}}也开始起作用了,获得了data


只剩下一行了
直接写字符串模板

模板是可以自己new一个的。不必要存在一个真实的html文件。可以只给出一个名字,这个名字是html文件后缀式的。比如:tmpl.html
请看代码

func Joeltemplate3(writer http.ResponseWriter, request *http.Request) {
    tmpl := `<!DOCTYPE html>
<html>
    <head>
        <meta http-qquiv="Content-Type" content="text/html; charset=utf-8">
        <title>Go Web Programming</title>
    </head>
    <body>
        {{.}}
    </body>
</html>
`
    t := template.New("tmpl.html")
    t, _ = t.Parse(tmpl)
    t.Execute(writer, "Hello Moon !!!")
}

在main里http.HandleFunc这个函数就可以了。

    http.HandleFunc("/mm/", JoelTempFunc.Joeltemplate3)
    server := http.Server{Addr:":8090"}
    server.ListenAndServe()

执行结果


直接写字符串模板的执行结果
执行模板

如果有多个模板,你只想在执行的时候,才确定执行某一个模板,就需要能够自由的制定要执行的模板。那么要使用到模板名。
当 template.ParseFiles 的参数有多个模板的时候,Execute 方法只会执行第一个模板

    t, _ := template.ParseFiles("./JoelTemplate/sayHello.html","./JoelTemplate/sayHelloSon.html")
    t.Execute(writer, "最爱的是明月")

如果要指定执行第二个模板,这个时候要使用模板名。第二个模板的模板名不包含模板的路径。而且,要想使用模板名指定执行模板的方式,就不能使用 Execute 了,而应该使用 ExecuteTemplate

func Joeltemplate4(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTemplate/sayHello.html","./JoelTemplate/sayHelloMoon.html")
    t.ExecuteTemplate(writer,"sayHelloMoon.html", "最爱的是明月")
}

sayHelloMoon.html模板代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>sayHello Title</title>
</head>
<body>
每天要问候:{{.}}
<hr>

</body>
</html>
执行模板
条件动作

根据条件选择要执行的动作,格式是

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

当然,if else end 的模式也是存在的。

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

根据这个逻辑,写一个随机数,以数字5为界,大于5或相反,显示不同的内容。
代码示例

//条件动作
func processIfHandler(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTempWeb/tmpl.html")
    rand.Seed(time.Now().Unix())
    t.ExecuteTemplate(writer, "tmpl", rand.Intn(10)>5)
}

在main里添加这个

http.HandleFunc("/processif/", processIfHandler)

模板这样写

{{define "tmpl"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
<h3>This is random than 5</h3>

{{if .}}
就是Moon 大于 5
{{else}}
不是Moon 小于等于 5
{{end}}
</body>
</html>
{{end}}

这里的模板调用有个技巧。因为模板文件 define 了名字为 tmpl,所以在加载模板的时候,要这样写

t.ExecuteTemplate(writer, "tmpl", rand.Intn(10)>5)
大于5

小于等于5
迭代动作

可以对数组、切片、映射或者通道进行迭代,在迭代循环内把{{.}}显示为当前迭代的元素。使用 range 关键字实现。像这样

{{range array}}
    这里是循环元素显示{{.}}
{{end}}

来个对星期的模板加载的迭代动作

//迭代动作
func processIterationHandler(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTempWeb/tmplrange.html")
    dayofweek := []string{"星期日","星期一","星期二","星期三","星期四","星期五","星期六",}
    t.ExecuteTemplate(writer, "range", dayofweek)
}

在main里要添加这个

http.HandleFunc("/processIteration/", processIterationHandler)

模板文件

{{define "range"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
<h3>This is range</h3>

<ul>
{{range .}}
    <li>{{.}}</li>
{{end}}
</ul>
<table style="border-collapse:collapse;">
{{range .}}
    <tr >
        <td style="border:1px solid orange;">{{.}}</td>
    </tr>
{{end}}
</table>

</body>
</html>
{{end}}

为了好看一点,加了点table样式。


即使你的服务器是windows,路径也是区分大小写的

也可以在模板中添加一个变化,判断有没有数据,没有数据的话显示一个特定的信息。这需要使用 else ,方式类似if else end,是 range else end。

{{define "range"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
<h3>This is range</h3>

<ul>
{{range .}}
    <li>{{.}}</li>
{{end}}
</ul>
<table style="border-collapse:collapse;">
{{range .}}
    <tr >
        <td style="border:1px solid orange;">{{.}}</td>
    </tr>
    {{else}}
    <tr >
        <td style="border:1px solid orange;">暂时没有数据...</td>
    </tr>
{{end}}
</table>

</body>
</html>
{{end}}

然后把函数修改一下,去掉数组里的数据。

//迭代动作
func processIterationHandler(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTempWeb/tmplrange.html")
    //dayofweek := []string{"星期日","星期一","星期二","星期三","星期四","星期五","星期六",}
    dayofweek := []string{}
    t.ExecuteTemplate(writer, "range", dayofweek)
}

执行结果是这样的
暂时没有数据
设置动作

可以在模板中重新设置参数的值。使用 with 关键字,代码结构像这样

{{with arg}}
    set to arg
{{end}}

也可以添加 else,结构像这样

{{with arg}}
    set to arg
{{else}}
   still old arg
{{end}}

现在看一下模板代码示例

{{define "with"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
<h3>This is with</h3>


<table style="border-collapse:collapse;">
{{with "Moon"}}
    <tr >
        <td style="border:1px solid orange;">爱{{.}}</td>
    </tr>
{{else}}
    <tr >
        <td style="border:1px solid orange;">原来的数据{{.}}</td>
    </tr>
{{end}}
</table>

</body>
</html>
{{end}}

执行结果


with "Moon"

如果with后面的内容是空的,那么就是执行else的内容了。

{{define "with"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
<h3>This is with</h3>


<table style="border-collapse:collapse;">
{{with ""}}
    <tr >
        <td style="border:1px solid orange;">爱{{.}}</td>
    </tr>
{{else}}
    <tr >
        <td style="border:1px solid orange;">原来的数据{{.}}</td>
    </tr>
{{end}}
</table>

</body>
</html>
{{end}}

with内容为空的执行结果

with ""

\color{#9900ff}{注意:}
如果你在模板中使用了 define 定义模板名,在代码中,就必须使用
t.ExecuteTemplate(writer,"arg", map[string]string{"星期一":"Monday"})这样的语法

参数、变量、管道

在go语言中,模板参数也可以是各种形式的值,比如:布尔值、整数、字符串、结构、结构中的一个字段、数组中的一个键、一个变量、一个方法(有且只有一个返回值或再加上一个返回错误)、一个函数、或者一个点(.)。
就好像这样的代码

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

在动作中设置变量,是以美元符号$开头,就像这样:

$variable := value

这样看似乎变量没什么大用,但是,如果是下面这样呢?

{{range $key, $value := . }}
    Key = {{ $key }}; Value = {{ $value }}
{{end}}

变量实际上对动作来说,是非常重要的。
参看示例代码

//参数、变量、管道
func processArgHandler(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTempWeb/tmplArg.html")
    t.ExecuteTemplate(writer,"arg", map[string]string{"星期一":"Monday"})
}

这里的data是一个map映射,要传递到模板tmplArg.html组合后返回html代码。
模板代码

{{define "arg"}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
<h3>This is Arg/value/channel</h3>

<table style="border-collapse:collapse;">
{{range $key, $value := .}}
    <tr>
        <td style="border:1px solid orange;">Key = {{ $key }};Value = {{ $value }}</td>
    </tr>
{{end}}
</table>

<h3>This is include</h3>

</body>
</html>
{{end}}

在main函数中加入下面的代码

http.HandleFunc("/processArg/", processArgHandler)

执行结果如下


参数、变量

这样写map当然在实际项目代码中不会这么弄了。那么,看一下这段代码

func Joeltemplate5(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTemplate/sayHelloDay.html")
    weekday := map[string]string{"Sunday":"星期日", "Monday":"星期一", "Tuesday":"星期二", "Wednesday":"星期三", "Thursday":"星期四", "Friday":"星期五", "Saturday":"星期六"}

    t.ExecuteTemplate(writer,"sayHelloDay.html", weekday)
}

这样,就会随机的把map中的key 和 value 都显示出来了。而且,代码更符合实际编码的习惯。在给值前对map内的元素进行编辑。
模板中的管道是多个有序串联起来的参数。参数以 “|” 符号分隔。管道允许用户将一个参数的输出传递给下一个参数。比如,一个值为浮点数的参数,在模板中进行格式化,就像这样

{{ 2014.10171695 | printf "%.2f"}}

执行后,会得到 2014.10
代码如下

func Joeltemplate6(writer http.ResponseWriter, request *http.Request) {
    t, _ := template.ParseFiles("./JoelTemplate/sayHelloPipe.html")
    /*cmd1 := exec.Command("love","shun","sun")
    stdout1, err := cmd1.StdinPipe()
    if err != nil {
        t.ExecuteTemplate(writer, "sayHelloPipe.html", err)
    }else{
        t.ExecuteTemplate(writer, "sayHelloPipe.html", stdout1)
    }*/
    var nf float64 = 2014.10171695
t.ExecuteTemplate(writer, "sayHelloPipe.html", nf)
}

模板文件sayHelloPipe.html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Go Web Programming</title>
</head>
<body>
  {{.|printf "%.2f"}}
</body>
</html>

在main中 http.HandleFunc("/uu/", JoelTempFunc.Joeltemplate6)
然后就可以在 路径 uu 里看到执行结果了


%.2f

然后,我没知道实际的值在小数点后面还有很多位,当需要显示更大精度的浮点数的时候,不需要修改 go 代码,只修改模板文件里的通道参数就可以了。

{{.|printf "%.2f"}}
改为
{{.|printf "%.4f"}}

保存模板后,执行结果是


%.4f

你也可以试试其他精度的表现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容