{"path":"126116","ref":"3.2.2 模板处理.md","title":"3.2.2 模板处理","content":"\n[TOC]\n\n# 模板处理\n\nbeego 的模板处理引擎采用的是 Go 内置的 `html\/template` 包进行处理,而且 beego 的模板处理逻辑是采用了缓存编译方式,也就是所有的模板会在 beego 应用启动的时候全部编译然后缓存在 map 里面。\n\n## 模板目录\n\nbeego 中默认的模板目录是 `views`,用户可以把模板文件放到该目录下,beego 会自动在该目录下的所有模板文件进行解析并缓存,开发模式下每次都会重新解析,不做缓存。当然,用户也可以通过如下的方式改变模板的目录(只能指定一个目录为模板目录):\n\n\tbeego.ViewsPath = \"myviewpath\"\n\n## 自动渲染\n\n用户无需手动的调用渲染输出模板,beego 会自动的在调用完相应的 method 方法之后调用 Render 函数,当然如果您的应用是不需要模板输出的,那么可以在配置文件或者在 `main.go` 中设置关闭自动渲染。\n\n配置文件配置如下:\n\n\tautorender = false\n\nmain.go 文件中设置如下:\n\n\tbeego.AutoRender = false\n\n## 模板标签\n\nGo 语言的默认模板采用了 `{{` 和 `}}` 作为左右标签,但是我们有时候在开发中可能界面是采用了 AngularJS 开发,他的模板也是这个标签,故而引起了冲突。在 beego 中你可以通过配置文件或者直接设置配置变量修改:\n\n\tbeego.TemplateLeft = \"<<<\"\n\tbeego.TemplateRight = \">>>\"\n\n## 模板数据\n\n模板中的数据是通过在 Controller 中 `this.Data` 获取的,所以如果你想在模板中获取内容 `{{.Content}}` ,那么你需要在 Controller 中如下设置:\n\n\tthis.Data[\"Content\"] = \"value\"\n\n如何使用各种类型的数据渲染:\n\n- 结构体\n\t\n\t结构体结构\n\n\t\ttype A struct{\n\t\t\tName string\n\t\t\tAge int\n\t\t}\n\t\n\t控制器数据赋值\n\t\t\t\n\t\tthis.Data[\"a\"]=&A{Name:\"astaxie\",Age:25}\n\t\t\n\t模板渲染数据如下:\n\t\n\t\tthe username is {{.a.Name}} \n\t\tthe age is {{.a.Age}}\n\t\t\t\n- map\n\t\n\t控制器数据赋值\n\t\n\t\tmp[\"name\"]=\"astaxie\"\n\t\tmp[\"nickname\"] = \"haha\"\n\t\tthis.Data[\"m\"]=mp\n\n\t模板渲染数据如下:\n\t\n\t\tthe username is {{.m.name}}\n\t\tthe username is {{.m.nickname}}\n\t\t\n- slice\n\n\t控制器数据赋值\n\t\n\t\tss :=[]string{\"a\",\"b\",\"c\"}\n\t\tthis.Data[\"s\"]=ss\n\t\n\t模板渲染数据如下:\n\t\n\t\t{{range $key, $val := .s}}\n\t\t{{$key}}\n\t\t{{$val}}\n\t {{end}}\t\n\n## 模板名称\n\nbeego 采用了 Go 语言内置的模板引擎,所有模板的语法和 Go 的一模一样,至于如何写模板文件,详细的请参考 [模板教程](https:\/\/github.com\/astaxie\/build-web-application-with-golang\/blob\/master\/zh\/07.4.md)。\n\n用户通过在 Controller 的对应方法中设置相应的模板名称,beego 会自动的在 viewpath 目录下查询该文件并渲染,例如下面的设置,beego 会在 admin 下面找 add.tpl 文件进行渲染:\n\n\tthis.TplName = \"admin\/add.tpl\"\n\n我们看到上面的模板后缀名是 tpl,beego 默认情况下支持 tpl 和 html 后缀名的模板文件,如果你的后缀名不是这两种,请进行如下设置:\n\n\tbeego.AddTemplateExt(\"你文件的后缀名\")\n\n当你设置了自动渲染,然后在你的 Controller 中没有设置任何的 TplName,那么 beego 会自动设置你的模板文件如下:\n\n\tc.TplName = strings.ToLower(c.controllerName) + \"\/\" + strings.ToLower(c.actionName) + \".\" + c.TplExt\n\n也就是你对应的 Controller 名字+请求方法名.模板后缀,也就是如果你的 Controller 名是 `AddController`,请求方法是 `POST`,默认的文件后缀是 `tpl`,那么就会默认请求 `\/viewpath\/AddController\/post.tpl` 文件。\n\n## Layout 设计\n\nbeego 支持 layout 设计,例如你在管理系统中,整个管理界面是固定的,只会变化中间的部分,那么你可以通过如下的设置:\n\n\tthis.Layout = \"admin\/layout.html\"\n\tthis.TplName = \"admin\/add.tpl\" \n\n在 layout.html 中你必须设置如下的变量:\n\n\t{{.LayoutContent}}\n \nbeego 就会首先解析 TplName 指定的文件,获取内容赋值给 LayoutContent,然后最后渲染 layout.html 文件。\n\n目前采用首先把目录下所有的文件进行缓存,所以用户还可以通过类似这样的方式实现 layout:\n\n\t{{template \"header.html\" .}}\n\tLogic code\n\t{{template \"footer.html\" .}}\n\n>>> 特别注意后面的`.`,这是传递当前参数到子模板\n\n## LayoutSection\n\n对于一个复杂的 `LayoutContent`,其中可能包括有javascript脚本、CSS 引用等,根据惯例,通常 css 会放到 Head 元素中,javascript 脚本需要放到 body 元素的末尾,而其它内容则根据需要放在合适的位置。在 `Layout` 页中仅有一个 `LayoutContent` 是不够的。所以在 `Controller` 中增加了一个 `LayoutSections`属性,可以允许 `Layout` 页中设置多个 `section`,然后每个 `section` 可以分别包含各自的子模板页。\n\nlayout_blog.tpl:\n\n```\n\n\n\n Lin Li<\/title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\">\n <link rel=\"stylesheet\" href=\"http:\/\/netdna.bootstrapcdn.com\/bootstrap\/3.0.3\/css\/bootstrap.min.css\">\n <link rel=\"stylesheet\" href=\"http:\/\/netdna.bootstrapcdn.com\/bootstrap\/3.0.3\/css\/bootstrap-theme.min.css\">\n {{.HtmlHead}}\n<\/head>\n<body>\n\n <div class=\"container\">\n {{.LayoutContent}}\n <\/div>\n <div>\n {{.SideBar}}\n <\/div>\n <script type=\"text\/javascript\" src=\"http:\/\/code.jquery.com\/jquery-2.0.3.min.js\"><\/script>\n <script src=\"http:\/\/netdna.bootstrapcdn.com\/bootstrap\/3.0.3\/js\/bootstrap.min.js\"><\/script>\n {{.Scripts}}\n<\/body>\n<\/html>\n```\n\nhtml_head.tpl:\n\n```\n<style>\n h1 {\n color: red;\n }\n<\/style>\n```\n \nscripts.tpl:\n\n```\n<script type=\"text\/javascript\">\n $(document).ready(function() {\n \/\/ bla bla bla\n });\n<\/script>\n```\n\n逻辑处理如下所示:\n\n```\ntype BlogsController struct {\n beego.Controller\n}\n\nfunc (this *BlogsController) Get() {\n this.Layout = \"layout_blog.tpl\"\n this.TplName = \"blogs\/index.tpl\"\n this.LayoutSections = make(map[string]string)\n this.LayoutSections[\"HtmlHead\"] = \"blogs\/html_head.tpl\"\n this.LayoutSections[\"Scripts\"] = \"blogs\/scripts.tpl\"\n this.LayoutSections[\"Sidebar\"] = \"\"\n}\n```\t\t\n\n## renderform 使用\n\n定义 struct:\n\n\ttype User struct {\n\t\tId int `form:\"-\"`\n\t\tName interface{} `form:\"username\"`\n\t\tAge int `form:\"age,text,年龄:\"`\n\t\tSex string\n\t\tIntro string `form:\",textarea\"`\n\t}\n\n* StructTag 的定义用的标签用为`form`,和 [ParseForm 方法](..\/controller\/params.md#%E7%9B%B4%E6%8E%A5%E8%A7%A3%E6%9E%90%E5%88%B0-struct) 共用一个标签,标签后面有三个可选参数,用`,`分割。第一个参数为表单中类型的`name`的值,如果为空,则以`struct field name`为值。第二个参数为表单组件的类型,如果为空,则为`text`。表单组件的标签默认为`struct field name`的值,否则为第三个值。\n* 如果`form`标签只有一个值,则为表单中类型`name`的值,除了最后一个值可以忽略外,其他位置的必须要有`,`号分割,如:`form:\",,姓名:\"`\n* 如果要忽略一个字段,有两种办法,一是:字段名小写开头,二是:`form` 标签的值设置为 `-`\n* 现在的代码版本只能实现固定的格式,用br标签实现换行,无法实现css和class等代码的插入。所以,要实现form的高级排版,不能使用renderform的方法,而需要手动处理每一个字段。\n\ncontroller:\n\n\tfunc (this *AddController) Get() {\n\t this.Data[\"Form\"] = &User{}\n\t this.TplName = \"index.tpl\"\n\t}\n\nForm 的参数必须是一个 struct 的指针。\n\ntemplate:\n\n\t<form action=\"\" method=\"post\">\n\t{{.Form | renderform}}\n\t<\/form>\n\n上面的代码生成的表单为:\n\t\n```\n\tName: <input name=\"username\" type=\"text\" value=\"test\"><\/br>\n\t年龄:<input name=\"age\" type=\"text\" value=\"0\"><\/br>\n\tSex: <input name=\"Sex\" type=\"text\" value=\"\"><\/br>\n\tIntro: <input name=\"Intro\" type=\"textarea\" value=\"\">\n```\t\n"}