4.Gin HTML 模板渲染
Gin HTML 模板渲染
1. 全部模板放在一个目录里面的配置方法
创建用于渲染的模板html
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<h2>渲染的内容: {{.title}}</h2>
</body>
</html>
templates/goods.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>商品页面</h1>
<h2>渲染的内容: {{.title}}</h2>
</body>
</html>
路由加载模板文件
// 加载模板文件
r.LoadHTMLGlob("templates/*")
渲染模板
// c.HTML 渲染模板
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{"title": "前台首页"})
})
r.GET("/goods", func(c *gin.Context) {
c.HTML(http.StatusOK, "goods.html", gin.H{"title": "商品页面"})
})
测试如下
访问 http://localhost:8000/index
访问 http://localhost:8000/goods
2. 模板放在不同目录里面的配置方法
Gin 框架中如果不同目录下面有同名模板的话, 我们需要使用下面方法加载模板
创建模板文件
注意:定义模板的时候需要通过 define 定义名称
templates/admin/index.html
{{ define "admin/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<h2>admin 渲染的内容: {{.title}}</h2>
</body>
</html>
{{end}}
templates/default/index.html
{{ define "default/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<h2>default 渲染的内容: {{.title}}</h2>
</body>
</html>
{{ end }}
路由加载模板文件
// 加载模板文件
r.LoadHTMLGlob("templates/**/*")
“”
**
表示为文件夹路径,**/*
表示为所有文件夹下的所有文件
渲染模板
// c.HTML 渲染模板
r.GET("default/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "default/index.html", gin.H{"title": "default前台首页"})
})
r.GET("admin/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html", gin.H{"title": "admin前台首页"})
})
r.GET("admin/goods", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/goods.html", gin.H{"title": "商品页面"})
})
测试如下
访问 http://localhost:8000/default/index
访问 http://localhost:8000/admin/index
3. gin 模板基本语法
{{.}}
输出数据
模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。
当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:
编写结构体、以及渲染模板
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
// 定义结构体
type UserInfo struct {
Name string
Age int
Gender string
}
func main() {
// 1.创建路由
r := gin.Default()
// 加载模板文件
r.LoadHTMLGlob("templates/**/*")
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
// c.HTML 渲染模板
r.GET("default/index", func(c *gin.Context) {
// 创建对象
userInfo := UserInfo{
Name: "李白",
Age: 30,
Gender: "male",
}
// 渲染模板
c.HTML(http.StatusOK, "default/index.html", gin.H{
"title": "default前台首页",
"userInfo": userInfo,
})
})
// 3.监听端口,默认在8080
// 监听并在 0.0.0.0:8080 上启动服务
// Run("里面不指定端口号默认为8080")
r.Run(":8000")
}
编写模板,使用渲染的参数
{{ define "default/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>index</h1>
<h2>default 渲染的内容: {{.title}}</h2>
<h3>渲染用户信息:{{.userInfo}}</h3>
<span>{{.userInfo.Name}}</span><br>
<span>{{.userInfo.Age}}</span><br>
<span>{{.userInfo.Gender}}</span><br>
</body>
</html>
{{ end }}
测试如下:
访问 http://localhost:8000/default/index
模板的注释
{{/* 模板的注释 */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。
变量
我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法
如下:
{{/* 保存传入模板的数据 */}}
<h4>{{$obj := .title}}</h4>
<h4>模板的变量title={{$obj}}</h4>
刷新页面,测试如下:
移除空格
有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所空白符号。
例如:
{{- .userInfo.Name -}}
测试效果如下:
注意:-要紧挨{{和}},同时与模板值之间需要使用空格分隔。
比较函数
布尔函数会将任何类型的零值视为假,其余视为真。
下面是定义为函数的二元比较运算的集合:
eq 如果 arg1 == arg2 则返回真
ne 如果 arg1 != arg2 则返回真
lt 如果 arg1 < arg2 则返回真
le 如果 arg1 <= arg2 则返回真
gt 如果 arg1 > arg2 则返回真
ge 如果 arg1 >= arg2 则返回真
示例如下:
{{/* 比较函数 */}}
{{ if eq .score 60}}
<h4>score=60分</h4>
{{end}}
测试如下:
条件判断
Go 模板语法中的条件判断有以下几种:
{{if pipeline}} T1 {{end}}
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
示例如下:
{{if gt .score 60}}
及格
{{else}}
不及格
{{end}}
{{if gt .score 90}}
优秀
{{else if gt .score 60}}
及格
{{else}}
不及格
{{end}}
range
Go 的模板语法中使用 range 关键字进行遍历,有以下两种写法,其中 传递 的值必须是数组、切片、字典或者通道。
语法:
{{range $key,$value := .obj}}
{{$value}}
{{end}}
如果 传递 的值其长度为 0,不会有任何输出
{{range $key,$value := .obj}}
{{$value}}
{{else}}
pipeline 的值其长度为 0
{{end}}
如果 pipeline 的值其长度为 0,则会执行 else 分支。
示例如下:
首先往模板传递一个数组:
// c.HTML 渲染模板
r.GET("default/index", func(c *gin.Context) {
// 创建对象
userInfo := UserInfo{
Name: "李白",
Age: 30,
Gender: "male",
}
// 渲染模板
c.HTML(http.StatusOK, "default/index.html", gin.H{
"title": "default前台首页",
"userInfo": userInfo,
"score": 60,
"hobby": []string{"吃饭", "睡觉", "写代码"},
})
})
在模板使用 range 渲染 hobby 参数:
{{/* 使用 range 关键字进行遍历 */}}
<h4>hobby={{.hobby}}</h4>
<ul>
{{range $key,$value := .hobby}}
<li>{{$value}}</li>
{{else}}
<li>hobby 的值其长度为 0</li>
{{end}}
</ul>
测试如下:
With
以前要输出数据:
<h3>渲染用户信息:</h3>
<span>{{.userInfo.Name}}</span><br>
<span>{{.userInfo.Age}}</span><br>
<span>{{.userInfo.Gender}}</span><br>
现在要输出数据:
{{with .userInfo}}
<h3>with用户信息</h3>
<span>{{.Name}}</span><br>
<span>{{.Age}}</span><br>
<span>{{.Gender}}</span><br>
{{end}}
“简单理解:相当于 var :=.user
”
预定义函数 (了解)
执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用 Funcs 方法添加函数到模板里。
预定义的全局函数如下:
and
函数返回它的第一个 empty 参数或者最后一个参数;
就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
返回第一个非 empty 参数或者最后一个参数;
亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
返回它的单个参数的布尔值的否定
len
返回它的参数的整数类型长度
index
执行结果为第一个参数以剩下的参数为索引/键指向的值;
如"index x 1 2 3"返回 x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print
即 fmt.Sprint
printf
即 fmt.Sprintf
println
即 fmt.Sprintln
html
返回与其参数的文本表示形式等效的转义 HTML。
这个函数在 html/template 中不可用。
urlquery
以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。
这个函数在 html/template 中不可用。
js
返回与其参数的文本表示形式等效的转义 JavaScript。
call
执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
如"call .X.Y 1 2"等价于 go 语言里的 dot.X.Y(1, 2);
其中 Y 是函数类型的字段或者字典的值,或者其他类似情况;
call 的第一个参数的执行结果必须是函数类型的值(和预定义函数如 print 明显不同);
该函数类型值必须有 1 到 2 个返回值,如果有 2 个则后一个必须是 error 接口类型;
如果有 2 个返回值的方法返回的 error 非 nil,模板执行会中断并返回给调用模板执行者该错误;
示例如下:
{{/* 预定义函数 */}}
<h4>title len: {{len .title}}</h4>
<h4>title hobby: {{len .hobby}}</h4>
<h4>index hobby 2: {{index .hobby 2}}</h4>
效果如下:
自定义模板函数
“我们经常有需要将时间转换格式的情况,下面我们使用自定义模板函数来演示。
”
定义时间转换格式函数
// 定义时间格式转换的方法
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}
func main() {
// 1.创建路由
r := gin.Default()
//注册全局模板函数 注意顺序,注册模板函数需要在加载模板上面
r.SetFuncMap(template.FuncMap{"formatDate": formatAsDate})
// 加载模板文件
r.LoadHTMLGlob("templates/**/*")
}
传递时间参数到模板
// c.HTML 渲染模板
r.GET("default/index", func(c *gin.Context) {
// 创建对象
userInfo := UserInfo{
Name: "李白",
Age: 30,
Gender: "male",
}
// 渲染模板
c.HTML(http.StatusOK, "default/index.html", gin.H{
"title": "default前台首页",
"userInfo": userInfo,
"score": 60,
"hobby": []string{"吃饭", "睡觉", "写代码"},
"now": time.Now(),
})
})
在模板使用自定义函数
{{/* 自定义函数 */}}
<h4>自定义时间转换函数: </h4>
{{.now | formatDate}} <br>
或者
{{formatDate .now }} <br>
效果如下
传递多个参数到自定义参数
在上面只传递了一个参数,如果需要传递多个参数,示例如下:
定义模板函数
// 定义打印信息的方法
func printlnMsg(str1 string, str2 string) string {
return fmt.Sprintf("%s.....%s", str1, str2)
}
//注册全局模板函数 注意顺序,注册模板函数需要在加载模板上面
r.SetFuncMap(template.FuncMap{
"formatDate": formatAsDate,
"printlnMsg": printlnMsg,
})
传递两个字符串到模板
在模板中使用自定义函数
{{/* 传入多个函数到自定义函数 */}}
{{printlnMsg .str1 .str2}}
效果如下
嵌套 template
新建 templates/public/page_head.html
{{ define "public/page_head.html" }}
<h1 style="background-color: black; color: aliceblue">这一个公共的标题, title={{.title}}</h1>
{{end}}
引入 template
分别在 admin/index.html 和 default/index.html 的顶部引入 page_head
{{/* 引入template */}}
{{template "public/page_head.html" .}}
1、引入的名字为 page_head.html 中定义的名字
2、引入的时候注意最后的点.
,表示传入该页面的参数
测试效果
访问 http://localhost:8000/admin/index
访问 http://localhost:8000/default/index