4.Gin HTML 模板渲染

4.Gin HTML 模板渲染

Gin HTML 模板渲染

1. 全部模板放在一个目录里面的配置方法

创建用于渲染的模板html

a6dfe2357ab2b782d458478e280f196d.png


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>
路由加载模板文件

f3cedf50b2a63d5effba9d69174390f7.png

// 加载模板文件
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

4f1a1e64f7de5654549ab67c1d8014e3.png


访问 http://localhost:8000/goods

7d3864702b1713cd252df88279a15bc5.png


2. 模板放在不同目录里面的配置方法

Gin 框架中如果不同目录下面有同名模板的话, 我们需要使用下面方法加载模板

创建模板文件

注意:定义模板的时候需要通过 define 定义名称

templates/admin/index.html

0780e74e29137054d5656570cb494a11.png
1695819574799
{{ 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

8c09fab9feee87b1b08653fba2f9a34e.png
1695819665405
{{ 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 }}
路由加载模板文件
8b0d00fcd6c4097d95b1cbe11d17a40f.png
1695819916477
// 加载模板文件
r.LoadHTMLGlob("templates/**/*")

**表示为文件夹路径, **/* 表示为所有文件夹下的所有文件

渲染模板
246116a647e239595ceb484d3615ae1f.png
1695819990290
// 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

51763b84d867cb93a4eb6f7684cb1269.png
1695820021336

访问 http://localhost:8000/admin/index

789aa541286bcf2e995d69bdd0d23da5.png
1695820038857

3. gin 模板基本语法

{{.}} 输出数据

模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。

当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:

编写结构体、以及渲染模板
38d17217f54c0d8a4d467ee11fb0bd1e.png
1695820992727
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")
}
编写模板,使用渲染的参数
3c86854d2b95c8a47dc0e450cfe1de6f.png
1695821060579
{{ 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

e93cb09ead4359b0c9102e37d9a389db.png
1695821107785
模板的注释
b23b3d356a64e241dd2a8e5123cd3268.png
1695821176846
{{/* 模板的注释  */}}

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

变量

我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法

如下:

f18d69d3ab6c81c6b10431d7183185bb.png
1695821326461
{{/* 保存传入模板的数据   */}}
<h4>{{$obj := .title}}</h4>
<h4>模板的变量title={{$obj}}</h4>

刷新页面,测试如下:

6953d86434681b3445fd744545e17d48.png
1695821384861
移除空格

有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所空白符号。

例如:

f835bc7dd7ef199b35025076b1d6685a.png
1695821566124
{{-        .userInfo.Name     -}}

测试效果如下:

71eca416fa4e6cb1a47bdc9b652a1d40.png
1695821611978

注意:-要紧挨{{和}},同时与模板值之间需要使用空格分隔。

比较函数

布尔函数会将任何类型的零值视为假,其余视为真。

下面是定义为函数的二元比较运算的集合:

eq 如果 arg1 == arg2 则返回真

ne 如果 arg1 != arg2 则返回真

lt 如果 arg1 < arg2 则返回真

le 如果 arg1 <= arg2 则返回真

gt 如果 arg1 > arg2 则返回真

ge 如果 arg1 >= arg2 则返回真

示例如下:
b2a3f8393f4b8b9f6086c38eb0a67234.png
1695821869829
{{/*  比较函数  */}}
{{ if eq .score 60}}
    <h4>score=60分</h4>
{{end}}
测试如下:
d75cafdabaa5f137473ded4f652fd932.png
1695821900767
条件判断

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 分支。

示例如下:

首先往模板传递一个数组:

31bb0ab16b1b6f6b064f3f7d178f880e.png
1695826297180
// 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 参数:

35d266ffa54a460d224d501733d3e1de.png
1695826334196
{{/* 使用 range 关键字进行遍历   */}}
<h4>hobby={{.hobby}}</h4>
<ul>
    {{range $key,$value := .hobby}}
        <li>{{$value}}</li>
    {{else}}
        <li>hobby 的值其长度为 0</li>
    {{end}}
</ul>
测试如下:
ee3c48618a1e87accddc909cf4bf56e7.png
1695826360165
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>

效果如下:

a9971b21bcd2c42b210a6102d670c79a.png
1695828064128
自定义模板函数

我们经常有需要将时间转换格式的情况,下面我们使用自定义模板函数来演示。

定义时间转换格式函数
02a090aebb8df6980e502b4709c7454b.png
1695828577887
// 定义时间格式转换的方法
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/**/*")
    
}
传递时间参数到模板
64be331f7abca71612432d90e2a3ee14.png
1695828631467
// 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(),
    })
})
在模板使用自定义函数
8b135e08358500c62cf74ec3c38945a5.png
1695828664347
{{/* 自定义函数   */}}
<h4>自定义时间转换函数: </h4>
{{.now | formatDate}} <br>
或者
{{formatDate .now }} <br>
效果如下
5ba2cb131e354e3de3d41da6039c7acb.png
1695828686465
传递多个参数到自定义参数

在上面只传递了一个参数,如果需要传递多个参数,示例如下:

定义模板函数
9c990a715b5eb3a900ebef2178995b33.png
1695958515629
// 定义打印信息的方法
func printlnMsg(str1 string, str2 string) string {
    return fmt.Sprintf("%s.....%s", str1, str2)
}
//注册全局模板函数 注意顺序,注册模板函数需要在加载模板上面
r.SetFuncMap(template.FuncMap{
    "formatDate": formatAsDate,
    "printlnMsg": printlnMsg,
})
传递两个字符串到模板
29bf7972e360c9e6b47e5dd570eeedb0.png
1695959125016
在模板中使用自定义函数
d9894ff3db77857a010232241b3699e8.png
1695959192909
{{/* 传入多个函数到自定义函数 */}}
{{printlnMsg .str1 .str2}}
效果如下
1ee93200b27f66a95cb02b84ce0aa869.png
1695959223832
嵌套 template
新建 templates/public/page_head.html
0511dd8ad849a394c96d5facc8607f13.png
1695960430181
{{ 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

d4344b1fcc61b3da216b4a59e68081e0.png
1695960580888
{{/*  引入template  */}}
{{template "public/page_head.html" .}}

1、引入的名字为 page_head.html 中定义的名字

2、引入的时候注意最后的点.,表示传入该页面的参数

测试效果

访问 http://localhost:8000/admin/index

705b9c45a169d39f686e97715716e953.png
1695960664016

访问 http://localhost:8000/default/index

f29d85e0acbbc2030989f7f4af9af972.png
1695960688670

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/174662.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何入驻抖音本地生活服务商,附上便捷流程!

抖音作为一款短视频社交媒体应用&#xff0c;已经成为全球范围内数以亿计的用户的首选。而在普及的同时&#xff0c;短视频领域也在不断拓展自身的业务领域&#xff0c;其中之一就是本地生活服务。继抖音本地生活服务之后支付宝、视频号也相继开展了本地生活服务&#xff0c;用…

如何在IAR软件中使用STLINK V2编译下载和调试stm8单片机

安装使用IAR后&#xff0c;如使用系统默认设置&#xff0c;往往很难正常实现用stlink v2来下载和调试stm8芯片&#xff0c;我的解决方法如下&#xff1a; 1、打开项目的options菜单&#xff1a; 2、在项目的选项菜单中选择ST-LINK作为调试工具&#xff1a; 3、选择额外的输出…

六、程序员指南:数据平面开发套件

PORT HOTPLUG FRAMEWORK 端口热插拔框架为DPDK应用程序提供在运行时附加和分离端口的能力。由于该框架依赖于PMD实现&#xff0c;PMD无法处理的端口超出了该框架的范围。此外&#xff0c;在从DPDK应用程序分离端口后&#xff0c;该框架不提供从系统中移除设备的方法。对于由物…

1、数仓模型概述

1、问&#xff1a;什么是数据模型&#xff1f; 数仓领域中的模型指的是数据模型&#xff0c;要和商业分析中的模型不同 数据模型就是数据组织和存储方法&#xff0c;它强调从业务、数据存取和使用的角度合理的存储数据 2、问&#xff1a;模型和表的区别&#xff1f; 表是数据物…

只有Target才有PDB

中间的OBJ的debug信息是放在Target里了。

2021秋招-面经

面经总结 微软STCA面试-面经 字节AI lab实习面试记录 腾讯PCG-腾讯新闻面试 百度(AIDU)-内容策略部门面试 百度(AIDU)-搜索策略-机器学习算法工程师 百度(AIDU)-知识图谱部门算法工程师(2020-07-08) 百度(AIDU)-NLP部门算法工程师(2020-07-10) 微软STCA面试-面经 2020-…

HarmonyOS ArkTS 基础组件的使用(四)

1 组件介绍 组件&#xff08;Component&#xff09;是界面搭建与显示的最小单位&#xff0c;HarmonyOS ArkUI声明式开发范式为开发者提供了丰富多样的UI组件&#xff0c;我们可以使用这些组件轻松的编写出更加丰富、漂亮的界面。 组件根据功能可以分为以下五大类&#xff1a;…

文章系列2:Unraveling the functional dark matter through global metagenomics

这篇文章发布于2023年10月nature。通讯作者是来自于 DOE Joint Genome Institute, Lawrence Berkeley National Laboratory, Berkeley, CA, USA. 背景介绍&目标 作者首先背景介绍了两种主流宏基因组分析方法&#xff0c;包括reads-based reference mapping&#xff08;eg…

8.Gin 自定义控制器

8.Gin 自定义控制器 前言 在上一篇路由文件抽离的过程中&#xff0c;我们发现接口的业务逻辑还写在路由配置中&#xff0c;如下&#xff1a; 1696385129126 但是如果业务逻辑比较多&#xff0c;如果写在路由之中&#xff0c;肯定不合适。 我们可以将业务逻辑抽离&#xff0c;单…

python实战—核心基础4(超市购物小票随机抽奖程序) lv1

目录 一、核心代码解释 二、代码 三、运行截图 一、核心代码解释 1、random() 函数 描述 random() 方法返回随机生成的一个实数&#xff0c;它在[0,1)范围内。 语法 以下是 random() 方法的语法: import randomrandom.random() 注意&#xff1a;random()是不能直接访问…

【高性能计算】CUDA,OpenCL,FPGA 加速,MPI

OpenCL OpenCL&#xff08;Open Computing Language&#xff09;是一种跨平台的GPU加速技术&#xff0c;由Khronos Group开发。OpenCL允许开发人员在不同的硬件平台上编写并行计算应用程序。 OpenCL使用C语言的子集来编写应用程序&#xff0c;并提供了一组API&#xff0c;可以…

Keil MDK 安装

0 Preface/Foreword 1 下载和安装 官网&#xff1a;Keil Embedded Development Tools for Arm, Cortex-M, Cortex-R4, 8051, C166, and 251 processor families. Keil MDK 下载链接&#xff1a;Keil MDK 1.1 下载 根据需求下载对应的Keil MDK edition。 不同的editions包括 …

TensorFlow实战教程(一)-TensorFlow环境部署

从本篇文章开始,作者正式开始研究Python深度学习、神经网络及人工智能相关知识。第一篇文章主要讲解神经网络基础概念,同时讲解TensorFlow2.0的安装过程及基础用法,主要结合作者之前的博客和"莫烦大神"的视频介绍,后面随着深入会讲解具体的项目及应用。基础性文章…

Python的安装及其python程序生成exe可执行程序

Python是一种高级编程语言&#xff0c;由Guido van Rossum在1989年12月首次发布。它具有简单易学、易读、易写的语法和强大的动态类型和垃圾回收机制。Python解释器是自由且开放源代码的软件&#xff0c;可以在各种操作系统&#xff08;如Linux、Windows、macOS等&#xff09;上…

基于Bagging集成学习方法的情绪分类预测模型研究(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

机器学习笔记 - 创建CNN + RNN + CTC损失的模型来识别图像中的文本

我们将创建一个具有CTC损失的卷积循环神经网络来实现我们的OCR识别模型。 一、数据集 我们将使用 Visual Geometry Group 提供的数据。 Visual Geometry Group - University of OxfordComputer Vision group from the University of Oxfordhttps://www.robots.ox.ac.uk/~vgg/d…

chromium114添加新的语言国际化支持

一、需求说明 需要chromium114支持新语言体系,例如藏语,蒙古语,苗语等 二、操作步骤 1. build/config/locales.gni修改 在all_chrome_locales变量中添加新的语种标识,如下图。 2. 添加编译文件,告诉浏览器在编译时需要加载和输出那些文件 尝试编译出现错误一提示。需要…

Linux socket编程(5):三次握手和四次挥手分析和SIGPIPE信号的处理

在我之前写的Wireshark抓包&#xff1a;理解TCP三次握手和四次挥手过程中&#xff0c;通过抓包分析了TCP传输的三次握手和四次挥手的过程。在这一节中&#xff0c;将分析在Linux中的三次握手和四次挥手的状态和过程&#xff0c;另外还有一个在我们编程过程中值得注意的SIGPIPE信…

《微信小程序开发从入门到实战》学习二十四

3.3.12开发创建投票多选投票页面 创建投票多选投票页面和创建单选投票页面没有区别&#xff0c;唯一区别仅在于向服务端发送数据时&#xff0c;告诉服务器这个投票是什么类型的投票。这个类型用三种数据类型表示都可以&#xff0c;分别如下所示&#xff1a; multiple:true/fa…

【计算机网络笔记】路由算法之距离向量路由算法

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…