四、Gin模板

文章详细介绍了Gin框架中模板的使用,包括变量渲染、控制结构(分支和循环)、变量赋值以及模板函数。通过示例代码展示了如何在Gin中处理模板,如渲染变量、进行条件判断和循环操作,以及自定义模板函数。此外,还讨论了模板的嵌套和继承,强调了模块化和复用性的重要性。

四、Gin模板

模板在web开发中⼴泛使⽤,它能够有效的将业务逻辑和页⾯逻辑分开,使代码可读性增强、并且更加容易理解和维护。 模板简单来说就是⼀个其中包涵占位变量表⽰动态的部分的⽂件,模板⽂件在经过动态赋值后,返回给⽤户。

4.1 、变量渲染

视图部分:

package main

import "github.com/gin-gonic/gin"

type Student struct {
	Name string
	Age  int
}

func main() {
	// 基于引擎对象,可以理解为路由对象,
	r := gin.Default()
	// 加载模版文件
	r.LoadHTMLGlob("templates/*")

	r.GET("/index", func(context *gin.Context) {
		context.HTML(200, "index.html", gin.H{
			"user":       "June",
			"state":      "在线",
			"booksSlice": []string{"西游记", "三国演义", "聊斋", "水浒传"},
			"stuMap": map[string]interface{}{
				"name":  "summer",
				"age":   23,
				"hobby": []string{"攀岩", "爬山", "跑步"},
			},
			"stuStruct": Student{Name: "June", Age: 27},
		})
	})
	// 启动:默认本机的8080端口
	r.Run()
}

模版html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>变量渲染</h3>
{{ . }}
<p>{{.booksSlice}}</p>
<p>{{index .booksSlice 0 }}</p>
<p>{{index .booksSlice 1}}</p>
<p>{{index .booksSlice 2}}</p>
<p>{{.stuMap}}</p>
<p>{{.stuMap.name}}</p>
<p>{{.stuStruct}}</p>
<p>{{.stuStruct.Name}}</p>

</body>
</html>

切片的深度查询依靠内置函数index,map对象和结构体对象的深度查询通过句点符实现!

4.2 、控制结构

(1)分支结构
{{if  pipeline}} T1 {{end}}
{{if  pipeline}} T1 {{else}} T0 {{end}}
{{if  pipeline}} T1 {{else if pipeline}} T0 {{end}}
  1. if类似与Go的if语句,它也具有单分⽀,多分⽀等多种结构,不同的是需要使⽤end关键字结束。

  2. 表达式为false的情况是各种数据对象的0值:数值0,指针或接口是nil,数组、slice、map或string则是len为0。

<h3>分支结构</h3>
{{if gt .stuStruct.Age 18 }}
<p>{{index .booksSlice 0 }}</p>
<p>{{index .booksSlice 1}}</p>
<p>{{index .booksSlice 2}}</p>
<p>{{index .booksSlice 3}}</p>
{{else}}
<p>校园区</p>
{{end}}
(2)循环结构

表达式为false的情况是各种数据对象的0值:数值0,指针或接口是nil,数组、slice、map或string则是len为0。 

{{range $value := .}} {{end}}
{{range $key,$value := .}} {{end}}
  1. 如果range中只赋值给一个变量,则这个变量是当前正在迭代元素的值。如果赋值给两个变量,则第一个变量是索引值,第二个变量是当前正在迭代元素的值。

  2. 变量名以$开头

<h3>循环结构</h3>
<p>四大名著</p>
{{range $index,$value := .booksSlice}}
<p>{{$index}} : {{$value}} </p>
{{end}}

<p>stuMap学生的所有爱好</p>
{{range $index,$value := .stuMap.hobby}}
<p>{{$index}} : {{$value}} </p>
{{end}}
(3)变量赋值

可以在template中定义变量:

// 未定义过的变量
{{$var := pipeline}}

. 是有作用域的

// 展示年龄大于rain的所有学生
// "students": []Student{{Name: "yuan", Age: 22}, {Name: "alvin", Age: 16}, {Name: "eric", Age: 23}},

// 错误写法
{{range $index,$student := .students}}

{{if gt $student.Age .stuMap.age }}   // 此时的.已经不是全局的对象,而是当前遍历的$student对象
<p>{{$student.Name}},{{$student.Age}}</p>
{{end}}

{{end}}

// 正确写法
{{$rainAge := .stuMap.age}}
{{range $index,$student := .students}}
{{if gt $student.Age $rainAge }}
<p>{{$student.Name}},{{$student.Age}}</p>
{{end}}
{{end}}
(4)注释

注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。

{{/* a comment */}}

{/ 一定要紧贴

4.3、Gin的模板函数

(1)默认模板函数

语法格式:

functionName [Argument...]

Argument参数是可选的,如果有多个参数,参数直接用空格发分隔。

函数名函数调用格式对应关系运算说明
eqeq arg1 arg2arg1 == arg2arg1等于arg2则返回true
nene arg1 arg2arg1 != arg2arg1不等于arg2则返回true
ltlt arg1 arg2arg1 < arg2arg1小于arg2则返回true
lele arg1 arg2arg1 <= arg2arg1小于等于arg2则返回true
gtgt arg1 arg2arg1 > arg2arg1大于arg2则返回true
gege arg1 arg2arg1 >= arg2arg1大于等于arg2则返回true
andand 表达式1 表达式2表达式1 && 表达式2表达式1和表达式2都为真的时候返回true
oror 表达式1 表达式2表达式1 || 表达式2表达式1和表达式2其中一个为真的时候返回true
notnot 表达式!表达式表达式为false则返回true, 反之返回false
indexindex arg 索引/键index x 2 即x[2]每个被索引的主体必须是数组、切片或者字典
lenlen arglen x 即x的元素个数用于计算数组大小
urlqueryurlquery argurlquery url用于url编码
(2)自定义模板函数

视图部分:

func main() {

	router := gin.Default()
	router.SetFuncMap(template.FuncMap{
		"add": func(x, y int) int {
			return x + y
		},
	})
	// 返回一个html页面
	router.LoadHTMLGlob("templates/*")
	router.GET("/index", index)
	router.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

模版部分:

{{add 2 3}}

4.4、嵌套与继承

Gin框架默认都是使用单模板,如果需要使用block template功能,可以通过"https://github.com/gin-contrib/multitemplate"库实现

import (
	"github.com/gin-contrib/multitemplate"
	"github.com/gin-gonic/gin"
)
(1) 嵌套:define和template

在实际项目中,我们不可能只有一个模板,一般来说都有很多个模板,而且这些模板也会共享一些公共的模板,这些公共的模板我们都可以定义成子模板,在需要的时候调用子模板,就可以将子模板的内容嵌入当前模板中。

提示:在项目中使用子模板,可以让项目模板具有模块化的能力,提高模块复用能力和可维护性。

define可以直接在待解析内容中定义一个模板,定义了模板之后,可以使用template这个action来执行模板。template有两种格式:

{{template "name"}}
{{template "name" pipeline}}

第一种是执行名为name和template,点击设置为nil,第二种是点"."设置为pipline的值,并执行名为name的template。可以将template看做是函数:

template("name)
template("name",pipeline)

示例:在t1页面中和t2页面中嵌入一个广告页面

package main

import (
	"github.com/gin-contrib/multitemplate"
	"github.com/gin-gonic/gin"
)

func createMyRender() multitemplate.Renderer {
	r := multitemplate.NewRenderer()
	r.AddFromFiles("t1.html", "templates/t1.html", "templates/adv.html")
	r.AddFromFiles("t2.html", "templates/t2.html", "templates/adv.html")
	return r
}

func main() {
	// 基于获取引擎对象,可以理解为路由对象
	r := gin.Default()
	// 加载模板文件
	//r.LoadHTMLGlob("templates/*")
	r.HTMLRender = createMyRender()

	r.GET("/test01", func(context *gin.Context) {
		context.HTML(200, "t1.html", nil)
	})
	r.GET("/test02", func(context *gin.Context) {
		context.HTML(200, "t2.html", nil)
	})

	// 启动:默认本机的8080端口
	r.Run()
}

t1.html,t2.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=9">
    <title>Go Web Programming</title>
</head>

<body>

<div>This is t1.html</div>
{{ template "adv.html" }}

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=9">
    <title>Go Web Programming</title>
</head>

<body>

<div>This is t2.html</div>
{{ template "adv.html" }}

</body>

</html>

 adv.html

<div style="background-color: rebeccapurple;line-height: 200px;color: white;text-align: center">
    这是一条广告信息<br/>
</div>
(2)继承:block块

根据官方文档的解释:block等价于define定义一个模板,并在"有需要"的地方执行这个模板,执行时将"."设置为pipeline的值。

但应该注意,block的第一个动作是执行名为name的模板,如果不存在,则在此处自动定义这个模板,并执行这个临时定义的模板。换句话说,block可以认为是设置一个默认模板。

例如:

{{block "T1" .}} one {{end}}

它首先表示{{template "T1" .}},也就是说先找到T1模板,如果T1存在,则执行找到的T1,如果没找到T1,则临时定义一个{{define "T1"}} one {{end}},并执行它。

在此文件中指定了要执行一个名为"content"的模板,但此文件中没有使用define定义该模板,所以需要在其它文件中定义名为content的模板。现在分别在两个文件中定义两个content模板:

{{ define "content" }}

{{ end }}

如果使用block,那么可以设置默认的content模板。例如将原本定义在blue.html中的content设置为默认模板。

模板解析原理

(3)继承案例

base.html

...

{{block "title" .}}
<title>Theme Template for Bootstrap</title>
{{end}}  

...

<div class="content">
  {{block "content" .}}
  {{end}}
</div>

student.html

{{template "base.html" .}}

{{define "title"}}
<title>学生管理</title>
{{end}}

{{define "content"}}
<h3>学生管理</h3>
{{end}}

course.html

{{template "base.html" .}}

{{define "title"}}
<title>课程管理</title>
{{end}}

{{define "content"}}
<h3>课程管理</h3>
{{end}}

class.html

{{template "base.html" .}}

{{define "content"}}
<h3>班级管理</h3>
{{end}}

{{define "title"}}
<title>班级管理</title>
{{end}}

main.go

package main

import (
	"github.com/gin-contrib/multitemplate"
	"github.com/gin-gonic/gin"
	"net/http"
)

func index(c *gin.Context) {
	c.HTML(http.StatusOK, "index", gin.H{})
}
func student(c *gin.Context) {
	c.HTML(http.StatusOK, "student", gin.H{})
}

func course(c *gin.Context) {
	c.HTML(http.StatusOK, "course", gin.H{})
}

func class(c *gin.Context) {
	c.HTML(http.StatusOK, "class", gin.H{})
}

func createMyRender() multitemplate.Renderer {
	r := multitemplate.NewRenderer()
	r.AddFromFiles("index", "templates/base.html", "templates/index.html")
	r.AddFromFiles("student", "templates/base.html", "templates/student.html")
	r.AddFromFiles("course", "templates/base.html", "templates/course.html")
	r.AddFromFiles("class", "templates/base.html", "templates/class.html")

	return r
}

func main() {

	router := gin.Default()
	// 返回一个html页面
	// router.LoadHTMLGlob("templates/*")  // 继承会发生block覆盖
	router.HTMLRender = createMyRender()
	router.GET("/", index)
	router.GET("/student", student)
	router.GET("/course", course)
	router.GET("/class", class)
	router.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
<think> 我们正在讨论Go的gin框架中模板的使用方法。根据引用内容,我们可以总结出以下关键步骤和注意事项: 1. **模板文件组织**:默认情况下,模板文件应放在项目目录的`templates`文件夹中。gin框架支持按照目录结构组织模板(引用[3])。 2. **模板语法**:在模板文件中使用`{{ .变量名 }}`来输出变量值,变量值由后端通过`gin.H`或结构体传入(引用[4])。 3. **模板加载**: - 使用`gin.Default()`创建默认的路由引擎。 - 调用`LoadHTMLGlob`方法加载模板,参数为模板文件的路径模式(引用[2]中`r.LoadHTMLGlob("/Users/.../templates/**/*")`)。也可以使用`LoadHTMLFiles`加载单个文件。 4. **静态文件服务**:使用`Static`方法设置静态文件目录(引用[2]中`r.Static("/xxx", ".../statics")`)。 5. **渲染模板**: - 在路由处理函数中,使用`c.HTML`方法渲染模板,参数为HTTP状态码、模板文件名(相对于`templates`目录的路径)和传入模板的数据(引用[2])。 ### 示例代码解析 引用[2]的main.go示例展示了: - 加载静态文件:`r.Static`将URL路径`/xxx`映射到本地的静态文件目录。 - 加载模板:`r.LoadHTMLGlob`使用通配符加载`templates`目录下所有子目录的模板文件。 - 定义路由:两个GET路由,分别渲染不同的模板,并传入不同的数据(通过`gin.H`)。 ### 模板文件示例 引用[1]和引用[4]分别展示了模板文件的内容: - 在模板中,使用`{{.}}`表示传入的整个数据对象(引用[1]),而`{{.变量名}}`表示传入数据对象的字段(引用[4])。 ### 步骤总结 1. 创建模板文件,放置在`templates`目录下(可含子目录)。 2. 在Go代码中: - 初始化gin引擎。 - 设置静态文件服务(如果需要)。 - 使用`LoadHTMLGlob`或`LoadHTMLFiles`加载模板。 - 定义路由,在路由处理函数中使用`c.HTML`渲染模板并传入数据。 ### 注意事项 - 模板文件名在渲染时需指定相对于`templates`目录的路径(包括子目录)。例如,模板文件`templates/users/index.tmpl`在渲染时使用`"users/index.tmpl"`。 - 传入模板的数据可以是`gin.H`(即`map[string]interface{}`)或结构体,结构体字段需大写(可导出)。 §§相关问题§§ 1. 如何在gin框架中嵌套模板(布局)? 2. gin框架模板支持自定义函数吗?如何实现? 3. 如何处理gin模板中的循环和条件判断? </think> ### Go Gin框架模板使用指南 以下是Gin框架中模板使用的核心步骤和要点: #### 1. **模板文件组织** - 模板文件必须放在`templates`目录下(默认约定),支持子目录结构: ```text templates/ ├── users/ │ └── index.tmpl ├── posts/ │ └── index.tmpl └── layouts/ └── default.html ``` 目录结构直接影响模板加载路径[^3]。 #### 2. **模板语法基础** - 变量渲染:`{{ .变量名 }}` ```html <h1>Welcome {{ .username }}</h1> <!-- 传入 gin.H{"username": "John"} --> ``` - 完整对象渲染:`{{ . }}` ```html <title>{{ .title }}</title> <!-- 引用[4]示例 --> ``` #### 3. **核心代码实现** ```go package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() // 加载模板(支持通配符) r.LoadHTMLGlob("templates/**/*") // 加载所有子目录模板[^2] // 设置静态文件目录 r.Static("/static", "./statics") // 访问URL: /static/css/style.css → 文件 ./statics/css/style.css // 路由定义 r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "index.tmpl", gin.H{ "title": "首页", // 模板中通过 {{ .title }} 访问 "data": map[string]interface{}{"key": "value"}, }) }) r.Run(":8080") } ``` #### 4. **关键方法说明** | 方法 | 作用 | 示例 | |------|------|------| | `LoadHTMLGlob()` | 批量加载模板 | `r.LoadHTMLGlob("templates/*.html")` | | `LoadHTMLFiles()` | 加载单个模板 | `r.LoadHTMLFiles("templates/user.html")` | | `c.HTML()` | 渲染模板 | `c.HTML(200, "template_name", data)` | | `Static()` | 注册静态目录 | `r.Static("/assets", "./static_files")` | #### 5. **高级用法** - **布局模板**:通过`{{ define }}`和`{{ template }}`实现复用 ```html <!-- templates/layouts/base.html --> <body> {{ template "content" . }} </body> <!-- templates/index.html --> {{ define "content" }} <h1>{{ .title }}</h1> {{ end }} ``` - **自定义模板函数**: ```go r.SetFuncMap(template.FuncMap{ "formatDate": func(t time.Time) string { return t.Format("2006-01-02") } }) ``` 模板中使用:`<p>{{ .createTime | formatDate }}</p>` #### 6. **常见问题解决方案** - **模板找不到**: - 检查路径:`LoadHTMLGlob("templates/**/*")`中的路径需绝对路径或正确相对路径 - 确认后缀:`.tmpl`/`.html`需与加载时一致 - **变量不渲染**: - 检查字段名匹配:`gin.H{"name": "Go"}` → 模板中用`{{ .name }}` - 确保字段可导出(结构体字段首字母大写) [^1]: 模板基础语法示例 [^2]: 核心路由和模板加载实现 [^3]: 模板目录结构规范 [^4]: 模板变量渲染语法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值