四、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参数是可选的,如果有多个参数,参数直接用空格发分隔。

函数名 函数调用格式 对应关系运算 说明
eq eq arg1 arg2 arg1 == arg2 arg1等于arg2则返回true
ne ne arg1 arg2 arg1 != arg2 arg1不等于arg2则返回true
lt lt arg1 arg2 arg1 < arg2 arg1小于arg2则返回true
le le arg1 arg2 arg1 <= arg2 arg1小于等于arg2则返回true
gt gt arg1 arg2 arg1 > arg2 arg1大于arg2则返回true
ge ge arg1 arg2 arg1 >= arg2 arg1大于等于arg2则返回true
and and 表达式1 表达式2 表达式1 && 表达式2 表达式1和表达式2都为真的时候返回true
or or 表达式1 表达式2 表达式1 || 表达式2 表达式1和表达式2其中一个为真的时候返回true
not not 表达式 !表达式 表达式为false则返回true, 反之返回false
index index arg 索引/键 index x 2 即x[2] 每个被索引的主体必须是数组、切片或者字典
len len arg len x 即x的元素个数 用于计算数组大小
urlquery urlquery arg urlquery 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 上启动服务
}

 

posted @ 2023-07-02 21:17  xiaohaoge  阅读(127)  评论(0编辑  收藏  举报