前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang 语言 Web 框架 beego v2 之模板

Golang 语言 Web 框架 beego v2 之模板

作者头像
frank.
发布2020-12-08 14:50:53
2K0
发布2020-12-08 14:50:53
举报
文章被收录于专栏:Golang语言开发栈

01

介绍

beego 的模板处理引擎采用的是 Go 内置的 html/template 包进行处理,而且 beego 的模板处理逻辑是采用了缓存编译方式,也就是所有的模板会在 beego 应用启动的时候全部编译然后缓存在 map 里面。

02

模板处理

模板目录

beego 的默认模板目录是 views,也可以通过 beego.ViewPath = "viewPath" 指定模板目录。beego 会自动解析并缓存模板目录中的所有模板文件。但是在开发模式下,每次修改模板文件都会重新解析,并且不做缓存。

自动渲染

beego 会在调用完相应 method 方法之后自动调用 Render 函数,不需要用户手动调用渲染输出模板。如果不需要模板输出,可以在配置文件中配置:

autorender=false

或在 main.go 文件中设置配置变量:

web.AutoRender = false

模板标签

beego 默认使用{{}}双大括号作为模板标签,但是可能会和某些模板引擎使用的标签冲突,beego 可以通过配置文件或者设置配置变量:

代码语言:javascript
复制
web.TemplateLeft="<<"
web.TemplateRight=">>"

模板数据

模板数据是通过在 controller中 this.Data 获取的,Data 字段的类型是:

代码语言:javascript
复制
Data map[interface{}]interface{}

key 和 value 都是空接口类型的 map

模板名称

自动渲染模式中,如果没有在 controller 中没有设置任何的模板名称TplName,beego 会自动将 controller 名字,method 名字,和模板后缀拼接成一个字符串,作为模板名称。

代码语言:javascript
复制
c.TplName = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt

beego 默认支持两种模板文件的后缀名,分别是 tpl 和 html。如果你的模板文件的后缀名不是这两种,需要设置配置变量:

web.AddTemplateExt("模板文件的后缀名")

用户还可以在 controller 中指定模板名称,beego 会自动在 viewPath 目录中查找该模板文件,设置方式如下:

代码语言:javascript
复制
this.TplName = "admin/add.tpl"

Layout 设计

beego 支持 Layout 设计,可以通过如下设置:

代码语言:javascript
复制
this.Layout = "admin/layout.html"
this.TplName = "admin/add.tpl"

在 layout.html 文件中,设置如下变量:

代码语言:javascript
复制
{{.LayoutContent}}

beego 就会首先解析 TplName 指定的文件,获取到内容后,赋值给 LayoutContent,最后渲染 layout.html 文件。

另外,还可以使用如下方式:

代码语言:javascript
复制
{{template "header.html" .}}
Logic code
{{template "footer.html" .}}

LayoutSection

根据惯例,js 文件放在 body 的末尾,css 文件放到 head 中,所以 layout.html 文件只有一个 LayoutContent 是不够用的。

所以,在 controller 中还有一个 LayoutSection 属性,可以给 Layout.html 设置多个 section,每个 section 都可以包含各自的子模板页面。

代码语言:javascript
复制
type BlogsController struct {
  web.Controller
}

func (this *BlogsController) Get() {
  this.Layout = "layout_blog.tpl"
  this.TplName = "blogs/index.tpl"
  this.LayoutSections = make(map[string]string)
  this.LayoutSections["HtmlHead"] = "blogs/html_head.tpl"
  this.LayoutSections["Scripts"] = "blogs/scripts.tpl"
  this.LayoutSections["Sidebar"] = "blogs/side_bar.tpl"
}

renderform 使用

struct:

代码语言:javascript
复制
type User struct {
  Id    int         `form:"-"`
  Name  interface{} `form:"username"`
  Age   int         `form:"age,text,年龄:"`
  Sex   string
  Intro string `form:",textarea"`
}

StructTag 的定义用的标签为 form,和 ParseForm 方法共用一个标签,标签后面有三个可选参数,用 , 分割。

第一个参数为表单中类型的 name 的值,如果为空,则以 struct field name 为值。

第二个参数为表单组件的类型,如果为空,则为 text。表单组件的标签默认为 struct field name 的值,否则为第三个值。

如果要忽略一个字段,有两种办法,一是:字段名小写开头,二是:form 标签的值设置为 -

如果 form 标签只有一个值,则为表单中类型 name 的值,除了最后一个值可以忽略外,其他位置的必须要有 ,号分割,如:form:",,姓名:"

现在的代码版本只能实现固定的格式,用 br 标签实现换行,无法实现 css 和 class 等代码的插入。所以,要实现 form 的高级排版,不能使用 renderform 的方法,而需要手动处理每一个字段。

controller:

代码语言:javascript
复制
func (this *AddController) Get() {
  this.Data["Form"] = &User{}
  this.TplName = "index.tpl"
}

Form 的参数必须是一个 struct 的指针。

template:

代码语言:javascript
复制
<form action="" method="post">
{{.Form | renderform}}
</form>

上面的代码生成的表单为:

代码语言:javascript
复制
Name: <input name="username" type="text" value="test"></br>
年龄:<input name="age" type="text" value="0"></br>
Sex: <input name="Sex" type="text" value=""></br>
Intro: <input name="Intro" type="textarea" value="">

03

模板语法

模板中支持的 go 语言符号

代码语言:javascript
复制
{{"string"}} // 一般 string
{{`raw string`}} // 原始 string
{{'c'}} // byte
{{print nil}} // nil 也被支持

模板中的 pipeline

可以是上下文的变量输出,也可以是函数通过管道传递的返回值

代码语言:javascript
复制
{{. | FuncA | FuncB | FuncC}}

当 pipeline 的值 false 或 0,nil 的指针或 interface,长度为 0 的 array、slice、map、string,那么这个 pipeline 被认为是空。

if ... else ... end

代码语言:javascript
复制
{{if pipeline}}{{end}}

if 判断时,pipeline 为空时,相当于判断为 false

代码语言:javascript
复制
this.Data["IsLogin"] = true
this.Data["IsHome"] = true
this.Data["IsAbout"] = true

支持嵌套的循环

代码语言:javascript
复制
{{if .IsHome}}
{{else}}
  {{if .IsAbout}}{{end}}
{{end}}

也可以使用 else if

代码语言:javascript
复制
{{if .IsHome}}
{{else if .IsAbout}}
{{else}}
{{end}}

range ... end

代码语言:javascript
复制
{{range pipeline}}{{.}}{{end}}

pipeline 支持的类型为 array,slice,map,channel

range 循环内部的「.」改变为以上类型的子元素

对应的值长度为 0 时,range 不会执行,「.」不会改变

代码语言:javascript
复制
pages := []struct {
  Num int
}{{10}, {20}, {30}}

this.Data["Total"] = 100
this.Data["Pages"] = pages

使用「.Num」输出子元素的 Num 属性,使用「$.」引用模板中的根级上下文

代码语言:javascript
复制
{{range .Pages}}
  {{.Num}} of {{$.Total}}
{{end}}

使用创建的变量,在这里和 go 中的 range 用法相同

代码语言:javascript
复制
{{range $index, $elem := .Pages}}
  {{$index}} - {{$elem.Num}} - {{.Num}} of {{$.Total}}
{{end}}

range 也支持 else

代码语言:javascript
复制
{{range .Pages}}
{{else}}
  {{/* 当 .Pages 为空 或者 长度为 0 时会执行这里 */}}
{{end}}

with ... end

代码语言:javascript
复制
{{with pipeline}}{{end}}

with 用于重定向 pipeline

代码语言:javascript
复制
{{with .Field.NestField.SubField}}
  {{.Var}}
{{end}}

也可以对变量赋值操作

代码语言:javascript
复制
{{with $value := "My name is %s"}}
  {{printf . "slene"}}
{{end}}

with 也支持 else

代码语言:javascript
复制
{{with pipeline}}
{{else}}
  {{/* 当 pipeline 为空时会执行这里 */}}
{{end}}

define

define 可以用来定义自模板,可用于模块定义和模板嵌套

代码语言:javascript
复制
{{define "loop"}}
  <li>{{.Name}}</li>
{{end}}

使用 template 调用模板

代码语言:javascript
复制
<ul>
  {{range .Items}}
    {{template "loop" .}}
  {{end}}
</ul>

template

代码语言:javascript
复制
{{template "模板名" pipeline}}

将对应的上下文 pipeline 传给模板,才可以在模板中调用

beego 中支持直接载入文件模板

代码语言:javascript
复制
{{template "path/to/head.html" .}}

beego 会依据你设置的模板路径读取 head.html

在模板中可以接着载入其他模板,对于模板的分模块处理很有用处

注释

允许多行文本注释,不允许嵌套

代码语言:javascript
复制
{{/* comment content
support new line */}}

04

模板函数

基本函数

变量可以使用符号 | 在函数间传递

代码语言:javascript
复制
{{.Con | markdown | addlinks}}
代码语言:javascript
复制
{{.Name | printf "%s"}}

使用括号

代码语言:javascript
复制
{{printf "nums is %s %d" (printf "%d %d" 1 2) 3}}

and

代码语言:javascript
复制
{{and .X .Y .Z}}

and 会逐一判断每个参数,将返回第一个为空的参数,否则就返回最后一个非空参数

or

代码语言:javascript
复制
{{or .X .Y .Z}}

or 会逐一判断每个参数,将返回第一个非空的参数,否则就返回最后一个参数

call

代码语言:javascript
复制
{{call .Field.Func .Arg1 .Arg2}}

call 可以调用函数,并传入参数

调用的函数需要返回 1 个值或者 2 个值,返回两个值时,第二个值用于返回 error 类型的错误。返回的错误不等于 nil 时,执行将终止。

Index

index 支持 map, slice, array, string,读取指定类型对应下标的值

代码语言:javascript
复制
this.Data["Maps"] = map[string]string{"name": "Beego"}
代码语言:javascript
复制
{{index .Maps "name"}}

len

代码语言:javascript
复制
{{printf "The content length is %d" (.Content|len)}}

返回对应类型的长度,支持类型:map, slice, array, string, chan

not

not 返回输入参数的否定值,if true then false else true

print

对应 fmt.Sprint

printf

对应 fmt.Sprintf

println

对应 fmt.Sprintln

urlquery

代码语言:javascript
复制
{{urlquery "http://beego.me"}}

将返回

代码语言:javascript
复制
http%3A%2F%2Fbeego.me

eq / ne / lt / le / gt/ ge

这类函数一般配合在 if 中使用

eq 和其他函数不一样的地方是,支持多个参数,和下面的逻辑判断相同

代码语言:javascript
复制
arg1==arg2 || arg1==arg3 || arg1==arg4 ...

与 if 一起使用

代码语言:javascript
复制
{{if eq true .Var1 .Var2 .Var3}}{{end}}
代码语言:javascript
复制
{{if lt 100 200}}{{end}}

内置模板函数

dateformat

date

compare

substr

html2str

str2html

htmlquote

htmlunquote

renderform

assets_js

assets_css

config

map_get

代码语言:javascript
复制
// In controller
Data["m"] = map[string]interface{} {
    "a": 1,
    "1": map[string]float64{
        "c": 4,
    },
}

// In view
{{ map_get .m "a" }} // return 1
{{ map_get .m 1 "c" }} // return 4

urlfor

代码语言:javascript
复制
{{urlfor "TestController.List"}}

自定义模板函数

beego 支持用户定义模板函数,但是必须在 web.Run() 调用之前,设置如下:

代码语言:javascript
复制
func hello(in string)(out string){
    out = in + "world"
    return
}

web.AddFuncMap("hi",hello)

定义之后你就可以在模板中这样使用了:

代码语言:javascript
复制
{{.Content | hi}}

05

静态文件

Go 语言内部其实已经提供了 http.ServeFile,通过这个函数可以实现静态文件的服务。beego 针对这个功能进行了一层封装,通过下面的方式进行静态文件注册:

代码语言:javascript
复制
web.SetStaticPath("/static","public")
  • 第一个参数是路径,url 路径信息
  • 第二个参数是静态文件目录(相对应用所在的目录)

beego 支持多个目录的静态文件注册,用户可以注册如下的静态文件目录:

代码语言:javascript
复制
web.SetStaticPath("/images","images")
web.SetStaticPath("/css","css")
web.SetStaticPath("/js","js")

设置了如上的静态目录之后,用户访问 /images/login/login.png,那么就会访问应用对应的目录下面的 images/login/login.png 文件。如果是访问 /static/img/logo.png,那么就访问 public/img/logo.png文件。

默认情况下 beego 会判断目录下文件是否存在,不存在直接返回 404 页面,如果请求的是 index.html,那么由于 http.ServeFile 默认是会跳转的,不提供该页面的显示。

因此 beego 可以设置

web.BConfig.WebConfig.DirectoryIndex=true 这样来使得显示 index.html 页面。而且开启该功能之后,用户访问目录就会显示该目录下所有的文件列表。

06

分页

这里所说的分页,指的是大量数据显示时,每页显示固定的数量的数据,同时显示多个分页链接,用户点击翻页链接或页码时进入到对应的网页。分页算法中需要处理的问题:

  1. 当前数据一共有多少条。
  2. 每页多少条,算出总页数。
  3. 根据总页数情况,处理翻页链接。
  4. 对页面上传入的 Get 或 Post 数据,需要从翻页链接中继续向后传。
  5. 在页面显示时,根据每页数量和当前传入的页码,设置查询的 Limit 和 Skip,选择需要的数据。
  6. 其他的操作,就是在 View 中显示翻页链接和数据列表的问题了。

模板处理过程中经常需要分页,那么如何进行有效的开发和操作呢?我们开发组针对这个需求开发了如下的例子,希望对大家有用

  • 工具类 https://github.com/beego/wetalk/blob/master/modules/utils/paginator.go
  • 模板 https://github.com/beego/wetalk/blob/master/views/base/paginator.html
  • 使用方法 https://github.com/beego/wetalk/blob/master/routers/base/base.go#L458

07

总结

本文整理了 beego 关于模板的使用方法,文章内容来自 beego 官方手册,作者提供的仅是 beego 模板的学习路径,读者也可以直接阅读官方手册。

参考资料:

https://beego.me/docs/intro/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go语言开发栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档