Orange 的視圖基于官方 `html/template` 和 `text/template` 包,基本用法也和官方包一致;
控制器 app.Context 和 view 包進行交互,http 渲染輸出由 app.Context 中的方法處理,具體渲染模版替換由 view 包進行處理。
## 建議
Golang 作為高性能,一切從簡單出發的編程變成語言,更多的服務于微服務相關應用,目前更多的是提供Api接口服務,模板渲染作為php5時代的產物以逐漸被前后端分離思想、vue、react等大前端所取代,這樣能讓后端開發更多的去關注服務本身,而不用再關注前端交互。
基于這樣的大背景,Orange 僅提供對 template 原生包的簡易封裝,能滿足簡單的模板渲染需求,如有更為復雜的的模板渲染需求,可以直接使用類似這一類的模板渲染包來實現 [goview](https://github.com/foolin/goview)。
## 模版定義
視圖包含應用的 HTML,并且將控制器 / 應用程序邏輯與演示邏輯分開。視圖文件默認存放于 `storage/views` 目錄下所有`.tpl`后綴的文件中,存放目錄具體配置可以在 `config/config/toml` 中進行調整。
```
[app]
...
viewPath = "./storage/views"
```
一個簡單的視圖如下所示:
```
<!-- 此視圖文件位置: storage/views/hello.tpl -->
<html>
<body>
<h1>Hello, {{.Name}}</h1>
</body>
</html>
```
在控制器中通過 `app.Context` 中的 `ViewHtml` 或 `ViewText` 方法進行渲染;
~~~
func ViewShow(c *app.Context) error {
showData := struct {
Name string
}{
Name : "world!",
}
return c.ViewHtml("hello", showData)
}
~~~
當然,視圖文件也可以嵌套在 storage/views 目錄的子目錄中。「點」符號可以用來引用嵌套視圖;如果你的視圖存儲在 storage/views/home/index.tpl,則可以這樣引用它:
```
return c.ViewHtml("home.index", showData)
```
## 向視圖傳遞數據
如上述例子所示,你可以使用結構體對象將數據傳遞到視圖:
```
return c.ViewHtml("home.index", showData)
```
此方式傳遞數據時,作為第二個參數的數據必須是結構體類型或 map[string]interface{} 類型;
## 獲取渲染內容
有些時候我們不想直接輸出模板內容,而是希望對內容再進行一些處理后輸出,可以直接調用 view 包中的方法獲取模板渲染后的內容。
~~~
import "gitee.com/zhucheer/orange/view"
...
tmpl := view.ContextTmpl(viewName).Assigns(viewData)
htmlRes, err := tmpl.Render()
~~~
該方法直接獲取模版渲染后內容字符串,處理邏輯有可開發人員自行處理,更新靈活。
## 模版引入
有些時候我們在定義模版時有些部分是可以共用的,比如后臺中的 header footer 部分模版可以將公共的部分拆分出來定義成一個個單獨的模板文件,在需要的地方進行 include 處理;
完成該功能需要2步:
1.定義引入模板
我們拆分出來的模板放到視圖目錄(views/),如下:
我們定義一個公用的樣式模板,放到 ./views/layout/baseStyle.tpl 下;
定義引入模版時需要添加 define 標記
~~~
{{define "baseStyle"}}
<style>
.redTitle{ font-size: 18px; color:#ff0000;}
</style>
{{end}}
~~~
然后我們在 控制器或中間件中將該模板進行申明:
~~~
func ActionName(c *app.Context) error {
// 同樣以「點」分割目錄形式輸入指定的模板片段文件
c.AddIncludeTmpl("layout.baseStyle")
}
~~~
2.模板引入
我們在渲染模板時,將該公用的樣式引入通過如下標記:
~~~
// 引用時使用 define定義的變量名
{{template "baseStyle" .}}
~~~
## 模版引擎
模板引擎基于官方包[html/template](https://studygolang.com/static/pkgdoc/pkg/html_template.htm) 和 [`text/template](https://studygolang.com/static/pkgdoc/pkg/text_template.htm) 語法操作也是一樣。
### 變量替換
~~~
func ViewShow(c *app.Context) error {
showData := struct {
Name string
}{
Name : "world!",
}
return c.ViewHtml("hello", showData)
}
...
視圖文件:
<html>
<body>
<h1>Hello, {{.Name}}</h1>
</body>
</html>
~~~
視圖文件中的 `{{.Name}}` 將會替換成 `world!`
### 系統變量
目前包如下系統變量:
{{.CSRF_TOKEN}} 表單提交 csrf_token 數據
{{.REQUEST_REFERER}} 頁面請求來源數據
### 原樣輸出
`ViewHtml` 方法會對變量中的 html 內容進行轉義防止 js 注入等安全問題,如果我們在確保安全的情況下希望原樣輸出 html 內容,可以使用模版內置函數進行處理;
```
<html>
<body>
<h1>Hello, {{.Name|unescaped}}</h1>
</body>
</html>
```
### 模板注釋
~~~
{{/* a comment */}}
注釋,執行時會忽略,可以多行;注釋不能嵌套,并且必須緊貼分界符始止,就像這里表示的一樣。
~~~
### if 條件判斷
最簡單的bool類型和字符串類型的判斷
```
{{if .condition}}
{{end}}
```
當.condition為bool類型的時候,則為true表示執行,當.condition為string類型的時候,則非空表示執行。
**else if嵌套用法**
```
{{if .condition1}}
{{else if .contition2}}
{{end}}
```
**eq 等于**
{{if eq .var1 .var2}}
{{end}}
**ne 不等于**
{{if ne .var1 .var2}}
{{end}}
**lt 小于 (less than)**
{{if lt .var1 .var2}}
{{end}}
**le 小于等于**
{{if le .var1 .var2}}
{{end}}
**gt 大于**
{{if gt .var1 .var2}}
{{end}}
**ge 大于等于**
{{if ge .var1 .var2}}
{{end}}
### 循環遍歷
template 還支持 range 循環來遍歷 map、slice 內的內容,在模板內定義變量需要加上`$`符號
語法為:
{{range $i,$v := .slice}}
{{end}}
~~~
showData := struct {
ItemArr []int
}{
ItemArr: []int{1, 2, 3, 4, 5},
}
return c.ViewHtml("home.index", showData)
...
<ul>
{{range $i, $v: = .ItemArr}}
<li>list item {{$i}}=>{{$v}}</li>
{{end}}
</ul>
~~~