mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
完成mvc视图功能的开发及基本测试
This commit is contained in:
12
README.MD
12
README.MD
@ -1,16 +1,16 @@
|
||||
Go Frame - 轻量级的Go语言开发框架。
|
||||
|
||||
# 安装
|
||||
## 安装
|
||||
```
|
||||
go get -u gitee.com/johng/gf
|
||||
````
|
||||
|
||||
# 使用
|
||||
## 使用
|
||||
```go
|
||||
import "gitee.com/johng/gf/g/xxx"
|
||||
```
|
||||
|
||||
# 说明
|
||||
## 说明
|
||||
.
|
||||
├── g 【框架目录】
|
||||
│ ├── container 【常用数据结构容器】
|
||||
@ -60,4 +60,8 @@ import "gitee.com/johng/gf/g/xxx"
|
||||
│
|
||||
├── geg 【框架示例】
|
||||
├── vendor 【第三方包】
|
||||
└── version.go 【版本信息】
|
||||
└── version.go 【版本信息】
|
||||
|
||||
## 配置
|
||||
键名 键值类型 配置说明
|
||||
Server名称.gf.mvc.view.path string 模板目录绝对路径
|
||||
|
||||
48
g/frame/gconfig/gconfig.go
Normal file
48
g/frame/gconfig/gconfig.go
Normal file
@ -0,0 +1,48 @@
|
||||
// 全局配置管理对象
|
||||
package gconfig
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gmap"
|
||||
"gitee.com/johng/gf/g/encoding/gjson"
|
||||
)
|
||||
|
||||
// 配置对象
|
||||
var config = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 获取配置
|
||||
func Get(k string) interface{} {
|
||||
return config.Get(k)
|
||||
}
|
||||
|
||||
func GetInt(k string) int {
|
||||
if v := config.Get(k); v != nil {
|
||||
if r, ok := v.(int); ok {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetString(k string) string {
|
||||
if v := config.Get(k); v != nil {
|
||||
if r, ok := v.(string); ok {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 适用于json文件配置,在设置的时候通过gjson进行解析后再保存
|
||||
func GetJson(k string) *gjson.Json {
|
||||
if v := config.Get(k); v != nil {
|
||||
if r, ok := v.(*gjson.Json); ok {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置配置
|
||||
func Set(k string, v interface{}) {
|
||||
config.Set(k, v)
|
||||
}
|
||||
@ -1,8 +1,46 @@
|
||||
// MVC控制器基类
|
||||
package gmvc
|
||||
|
||||
import "gitee.com/johng/gf/g/net/ghttp"
|
||||
import (
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
"gitee.com/johng/gf/g/net/gsession"
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_SESSION_ID_NAME = "gfsessionid"
|
||||
)
|
||||
|
||||
// 控制器基类
|
||||
type Controller struct {
|
||||
ghttp.ControllerBase
|
||||
Server *ghttp.Server // Web Server对象
|
||||
Request *ghttp.ClientRequest // 请求数据对象
|
||||
Response *ghttp.ServerResponse // 返回数据对象
|
||||
Cookie *ghttp.Cookie // COOKIE操作对象
|
||||
Session *gsession.Session // SESSION操作对象
|
||||
View *View // 视图对象
|
||||
}
|
||||
|
||||
// 控制器初始化
|
||||
func (c *Controller) Init(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) {
|
||||
c.Server = s
|
||||
c.Request = r
|
||||
c.Response = w
|
||||
c.Cookie = ghttp.NewCookie(c.Request, c.Response)
|
||||
c.View = NewView(c)
|
||||
if r := c.Cookie.Get(gDEFAULT_SESSION_ID_NAME); r != "" {
|
||||
c.Session = gsession.Get(r)
|
||||
} else {
|
||||
c.Session = gsession.Get(gsession.Id())
|
||||
}
|
||||
}
|
||||
|
||||
// 控制器结束请求
|
||||
func (c *Controller) Shut() {
|
||||
if c.Cookie.Get(gDEFAULT_SESSION_ID_NAME) == "" {
|
||||
c.Cookie.Set(gDEFAULT_SESSION_ID_NAME, c.Session.Id())
|
||||
}
|
||||
c.Cookie.Output()
|
||||
c.Response.Output()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,115 +1,78 @@
|
||||
package gmvc
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gmap"
|
||||
"html/template"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"sync"
|
||||
"strings"
|
||||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"gitee.com/johng/gf/g/os/gview"
|
||||
"gitee.com/johng/gf/g/frame/gconfig"
|
||||
)
|
||||
|
||||
// 视图对象
|
||||
// 视图对象(一个请求一个视图对象,用完即销毁)
|
||||
type View struct {
|
||||
mu sync.RWMutex
|
||||
path string // 模板目录(绝对路径)
|
||||
tpls *gmap.StringInterfaceMap // 已解析的模板对象指针,防止重复解析
|
||||
suffix string // 模板文件名后缀
|
||||
mu sync.RWMutex // 并发互斥锁
|
||||
ctl *Controller // 所属控制器
|
||||
view *gview.View // 底层视图对象
|
||||
data map[string]interface{} // 视图数据
|
||||
}
|
||||
|
||||
// 模板对象
|
||||
type Template struct {
|
||||
mu sync.RWMutex // 并发互斥锁
|
||||
path string // 模板文件(绝对路径)
|
||||
data map[string]interface{} // 全局的模板变量
|
||||
content string // 模板内容(解析之后保存到内存中)
|
||||
template *template.Template // 底层模板对象
|
||||
lasterror error // 最近一次错误
|
||||
}
|
||||
|
||||
// 生成一个视图对象
|
||||
func NewView(path string) *View {
|
||||
// 创建一个MVC请求中使用的视图对象
|
||||
func NewView(c *Controller) *View {
|
||||
path := c.Server.GetName() + ".gf.mvc.view.path"
|
||||
return &View{
|
||||
path : path,
|
||||
tpls : gmap.NewStringInterfaceMap(),
|
||||
suffix : "tpl",
|
||||
ctl : c,
|
||||
view : gview.GetView(gconfig.GetString(path)),
|
||||
data : make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// 设置模板文件后缀名
|
||||
func (view *View) SetSuffix(suffix string) {
|
||||
view.mu.Lock()
|
||||
defer view.mu.Unlock()
|
||||
view.suffix = suffix
|
||||
}
|
||||
|
||||
// 获取模板文件后缀名
|
||||
func (view *View) GetSuffix() string {
|
||||
view.mu.Lock()
|
||||
defer view.mu.Unlock()
|
||||
return view.suffix
|
||||
}
|
||||
|
||||
// 根据文件名称生成一个模板对象,或者获取一个现有的模板对象
|
||||
func (view *View) Template(file string) (*Template, error) {
|
||||
path := strings.TrimRight(view.path, gfile.Separator) + gfile.Separator + file + "." + view.GetSuffix()
|
||||
if t := view.tpls.Get(path); t != nil {
|
||||
return t.(*Template), nil
|
||||
}
|
||||
if !gfile.Exists(path) {
|
||||
return nil, errors.New("template '" + path + "' does not exist")
|
||||
}
|
||||
if !gfile.IsReadable(path) {
|
||||
return nil, errors.New("template '" + path + "' is not readable")
|
||||
}
|
||||
t := &Template{
|
||||
path : path,
|
||||
data : make(map[string]interface{}),
|
||||
content : gfile.GetContents(path),
|
||||
template : template.New(path),
|
||||
}
|
||||
view.tpls.Set(path, t)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
func (t *Template) BindFunc(name string, function interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.template.Funcs(template.FuncMap{name : function})
|
||||
}
|
||||
|
||||
// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
func (t *Template) Assigns(data map[string]interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
func (view *View) Assigns(data map[string]interface{}) {
|
||||
view.mu.Lock()
|
||||
defer view.mu.Unlock()
|
||||
for k, v := range data {
|
||||
t.data[k] = v
|
||||
view.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
func (t *Template) Assign(k string, v interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.data[k] = v
|
||||
func (view *View) Assign(key string, value interface{}) {
|
||||
view.mu.Lock()
|
||||
defer view.mu.Unlock()
|
||||
view.data[key] = value
|
||||
}
|
||||
|
||||
// 返回解析后的模板内容,可以额外指定模板变量,如果没有可以传入nil
|
||||
func (t *Template) Parse(data map[string]interface{}) (string, error) {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if tpl, err := t.template.Parse(t.content); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
m := t.data
|
||||
for k, v := range data {
|
||||
m[k] = v
|
||||
}
|
||||
if err := tpl.Execute(buffer, m); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 解析指定模板
|
||||
func (view *View) Display(file string) error {
|
||||
// 查询模板
|
||||
tpl, err := view.view.Template(file)
|
||||
if err != nil {
|
||||
view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error())
|
||||
return err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
// 绑定函数
|
||||
tpl.BindFunc("include", view.funcInclude)
|
||||
// 执行解析
|
||||
view.mu.RLock()
|
||||
defer view.mu.RUnlock()
|
||||
content, err := tpl.Parse(view.data)
|
||||
if err != nil {
|
||||
view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error())
|
||||
return err
|
||||
} else {
|
||||
view.ctl.Response.WriteString(content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 模板内置方法:include
|
||||
func (view *View) funcInclude(file string) template.HTML {
|
||||
tpl, err := view.view.Template(file)
|
||||
if err != nil {
|
||||
return template.HTML(err.Error())
|
||||
}
|
||||
content, err := tpl.Parse(view.data)
|
||||
if err != nil {
|
||||
return template.HTML(err.Error())
|
||||
}
|
||||
return template.HTML(content)
|
||||
}
|
||||
|
||||
@ -3,6 +3,6 @@ package ghttp
|
||||
|
||||
// 控制器接口
|
||||
type Controller interface {
|
||||
Init(*ClientRequest, *ServerResponse)
|
||||
Init(*Server, *ClientRequest, *ServerResponse)
|
||||
Shut()
|
||||
}
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/net/gsession"
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_SESSION_ID_NAME = "gfsessionid"
|
||||
)
|
||||
|
||||
// 控制器基类
|
||||
type ControllerBase struct {
|
||||
Request *ClientRequest
|
||||
Response *ServerResponse
|
||||
Cookie *Cookie
|
||||
Session *gsession.Session
|
||||
}
|
||||
|
||||
// 控制器初始化
|
||||
func (c *ControllerBase) Init(r *ClientRequest, w *ServerResponse) {
|
||||
c.Request = r
|
||||
c.Response = w
|
||||
c.Cookie = NewCookie(c.Request, c.Response)
|
||||
if r := c.Cookie.Get(gDEFAULT_SESSION_ID_NAME); r != "" {
|
||||
c.Session = gsession.Get(r)
|
||||
} else {
|
||||
c.Session = gsession.Get(gsession.Id())
|
||||
}
|
||||
}
|
||||
|
||||
// 控制器结束请求
|
||||
func (c *ControllerBase) Shut() {
|
||||
if c.Cookie.Get(gDEFAULT_SESSION_ID_NAME) == "" {
|
||||
c.Cookie.Set(gDEFAULT_SESSION_ID_NAME, c.Session.Id())
|
||||
}
|
||||
c.Cookie.Output()
|
||||
c.Response.Output()
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ type HandlerFunc struct {
|
||||
}
|
||||
|
||||
// Server表,用以存储和检索名称与Server对象之间的关联关系
|
||||
var serverMapping *gmap.StringInterfaceMap = gmap.NewStringInterfaceMap()
|
||||
var serverMapping = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 创建一个默认配置的HTTP Server(默认监听端口是80)
|
||||
func GetServer(name string) (*Server) {
|
||||
@ -81,6 +81,11 @@ func (s *Server) Run() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取
|
||||
func (s *Server) GetName() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
// http server setting设置
|
||||
// 注意使用该方法进行http server配置时,需要配置所有的配置项,否则没有配置的属性将会默认变量为空
|
||||
func (s *Server)SetConfig(c ServerConfig) error {
|
||||
|
||||
@ -12,7 +12,7 @@ type Domain struct {
|
||||
}
|
||||
|
||||
// 域名对象表,用以存储和检索域名(支持多域名)与域名对象之间的关联关系
|
||||
var domains *gmap.StringInterfaceMap = gmap.NewStringInterfaceMap()
|
||||
var domains = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 生成一个域名对象
|
||||
func (s *Server) Domain(domain string) *Domain {
|
||||
|
||||
@ -21,7 +21,7 @@ func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) {
|
||||
// 执行处理HTTP请求
|
||||
func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
request := &ClientRequest{}
|
||||
response := &ServerResponse {server : s}
|
||||
response := &ServerResponse{}
|
||||
request.Request = *r
|
||||
response.ResponseWriter = w
|
||||
if h := s.getHandler(gDEFAULT_DOMAIN, r.Method, r.URL.Path); h != nil {
|
||||
@ -38,7 +38,7 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
// 初始化控制器
|
||||
func (s *Server)initController(h *HandlerFunc, r *ClientRequest, w *ServerResponse) {
|
||||
c := reflect.New(h.ctype)
|
||||
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(r), reflect.ValueOf(w)})
|
||||
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(s), reflect.ValueOf(r), reflect.ValueOf(w)})
|
||||
c.MethodByName(h.fname).Call(nil)
|
||||
c.MethodByName("Shut").Call(nil)
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
type ServerResponse struct {
|
||||
http.ResponseWriter
|
||||
bufmu sync.RWMutex // 缓冲区互斥锁
|
||||
server *Server // 所属Server对象
|
||||
buffer []byte // 每个请求的返回数据缓冲区
|
||||
}
|
||||
|
||||
|
||||
130
g/os/gview/gview.go
Normal file
130
g/os/gview/gview.go
Normal file
@ -0,0 +1,130 @@
|
||||
package gview
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gmap"
|
||||
"html/template"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"sync"
|
||||
"strings"
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// 视图对象
|
||||
type View struct {
|
||||
mu sync.RWMutex
|
||||
path string // 模板目录(绝对路径)
|
||||
tpls *gmap.StringInterfaceMap // 已解析的模板对象指针,防止重复解析
|
||||
suffix string // 模板文件名后缀
|
||||
}
|
||||
|
||||
// 模板对象
|
||||
type Template struct {
|
||||
mu sync.RWMutex // 并发互斥锁
|
||||
path string // 模板文件(绝对路径)
|
||||
data map[string]interface{} // 全局的模板变量
|
||||
content string // 模板文件内容(解析之后保存到内存中)
|
||||
funcmap map[string]interface{} // FuncMap
|
||||
}
|
||||
|
||||
// 视图表
|
||||
var viewMap = gmap.NewStringInterfaceMap()
|
||||
|
||||
// 获取或者创建一个视图对象
|
||||
func GetView(path string) *View {
|
||||
if r := viewMap.Get(path); r != nil {
|
||||
return r.(*View)
|
||||
}
|
||||
v := New(path)
|
||||
viewMap.Set(path, v)
|
||||
return v
|
||||
}
|
||||
|
||||
// 生成一个视图对象
|
||||
func New(path string) *View {
|
||||
return &View{
|
||||
path : path,
|
||||
tpls : gmap.NewStringInterfaceMap(),
|
||||
suffix : "tpl",
|
||||
}
|
||||
}
|
||||
|
||||
// 设置模板文件后缀名
|
||||
//func (view *View) SetSuffix(suffix string) {
|
||||
// view.mu.Lock()
|
||||
// defer view.mu.Unlock()
|
||||
// view.suffix = suffix
|
||||
//}
|
||||
|
||||
// 获取模板文件后缀名
|
||||
func (view *View) GetSuffix() string {
|
||||
view.mu.RLock()
|
||||
defer view.mu.RUnlock()
|
||||
return view.suffix
|
||||
}
|
||||
|
||||
// 根据文件名称生成一个模板对象,或者获取一个现有的模板对象
|
||||
func (view *View) Template(file string) (*Template, error) {
|
||||
path := strings.TrimRight(view.path, gfile.Separator) + gfile.Separator + file + "." + view.GetSuffix()
|
||||
if t := view.tpls.Get(path); t != nil {
|
||||
return t.(*Template), nil
|
||||
}
|
||||
if !gfile.Exists(path) {
|
||||
return nil, errors.New("template '" + path + "' does not exist")
|
||||
}
|
||||
if !gfile.IsReadable(path) {
|
||||
return nil, errors.New("template '" + path + "' is not readable")
|
||||
}
|
||||
t := &Template{
|
||||
path : path,
|
||||
data : make(map[string]interface{}),
|
||||
content : gfile.GetContents(path),
|
||||
funcmap : make(map[string]interface{}),
|
||||
}
|
||||
view.tpls.Set(path, t)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
func (t *Template) BindFunc(name string, function interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.funcmap[name] = function
|
||||
}
|
||||
|
||||
// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
func (t *Template) Assigns(data map[string]interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
for k, v := range data {
|
||||
t.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
||||
func (t *Template) Assign(k string, v interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.data[k] = v
|
||||
}
|
||||
|
||||
// 返回解析后的模板内容,可以额外指定模板变量,如果没有可以传入nil
|
||||
// 函数内部的底层template必须每次调用都新生成一个,防止错误:html/template: cannot Parse after Execute
|
||||
func (t *Template) Parse(data map[string]interface{}) (string, error) {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if tpl, err := template.New(t.path).Funcs(t.funcmap).Parse(t.content); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
m := t.data
|
||||
for k, v := range data {
|
||||
m[k] = v
|
||||
}
|
||||
if err := tpl.Execute(buffer, m); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package user
|
||||
import (
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
"gitee.com/johng/gf/g/frame/gmvc"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 定义业务相关的控制器对象
|
||||
@ -22,17 +23,11 @@ func init() {
|
||||
|
||||
// 定义操作逻辑
|
||||
func (c *ControllerUser) Info() {
|
||||
//c.Session.Set("name", "john")
|
||||
c.Response.WriteString("session:" + c.Session.GetString("name"))
|
||||
c.Response.Write([]byte("user information page"))
|
||||
//t, err := template.New("test").Funcs(template.FuncMap{"add": Add}).Parse(gfile.GetContents("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view/user/info.tpl"))
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//}
|
||||
//
|
||||
//t.Execute(w.ResponseWriter, map[string]string{
|
||||
// "name" : "john",
|
||||
//})
|
||||
//c.Response.WriteString("user information page")
|
||||
c.View.Assign("name", "john")
|
||||
if err := c.View.Display("user/index"); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -4,9 +4,11 @@ import (
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
_ "gitee.com/johng/gf/geg/frame/mvc/controller/user"
|
||||
|
||||
"gitee.com/johng/gf/g/frame/gconfig"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gconfig.Set("johng.gf.mvc.view.path", "/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view")
|
||||
ghttp.GetServer("johng").SetAddr(":8199")
|
||||
ghttp.GetServer("johng").Run()
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
<div>footer</div>
|
||||
<h3>This is footer</h3>
|
||||
<div style="color:red">tpl vals: {{.}}</div>
|
||||
@ -4,7 +4,8 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>age:{{.}}</p>
|
||||
<p>add:{{add 1 2}}</p>
|
||||
<h3>This is index</h3>
|
||||
<p>tpl vals: {{.}}</p>
|
||||
{{include "user/footer"}}
|
||||
</body>
|
||||
</html>
|
||||
@ -1,13 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/os/gview"
|
||||
)
|
||||
|
||||
type B struct {
|
||||
|
||||
}
|
||||
|
||||
func add() int {return 1}
|
||||
|
||||
func main() {
|
||||
s := gtime.Second()
|
||||
t := time.Unix(s, 0)
|
||||
fmt.Println(t.Format("2006-01-02 15:04:05"))
|
||||
view := gview.New("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view/user/")
|
||||
tpl, _ := view.Template("info")
|
||||
tpl.BindFunc("include", add)
|
||||
fmt.Println(tpl.Parse(nil))
|
||||
//t, err := template.New("text").Funcs(template.FuncMap{"add":add}).Parse(`{{add 1 2}}`)
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//t.Execute(os.Stdout, u)
|
||||
}
|
||||
Reference in New Issue
Block a user