├── example └── app │ └── views │ ├── blogs │ ├── _header.html │ ├── show.html │ └── index.html │ └── layouts │ └── base.html ├── README.md └── ez-gin-template.go /example/app/views/blogs/_header.html: -------------------------------------------------------------------------------- 1 | {{define "header"}} 2 |

Hi, This is Header

3 | {{ end }} 4 | -------------------------------------------------------------------------------- /example/app/views/blogs/show.html: -------------------------------------------------------------------------------- 1 | {{ define "content" }} 2 |

{{ .blog.Title }}

3 | 4 |
5 | {{ .blog.Body }} 6 |
7 | {{ end }} 8 | -------------------------------------------------------------------------------- /example/app/views/blogs/index.html: -------------------------------------------------------------------------------- 1 | {{ define "content" }} 2 | {{ range $key, $blog := .blogs }} 3 |

4 | {{ $blog.Title }} 5 |

6 | 7 | {{template "header" .}} 8 | {{ end }} 9 | {{ end }} 10 | -------------------------------------------------------------------------------- /example/app/views/layouts/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | {{ template "content" . }} 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy Gin Template 2 | 3 | When i start using Gin i struggling with template system. I find out many people have the same problem. 4 | 5 | I have found some code in Github ([multitemplate.go](https://github.com/gin-gonic/contrib/tree/master/renders/multitemplate), [gin_html_render.go](https://gist.github.com/madhums/4340cbeb36871e227905)) which help but not everything i need is supported like Template helpers. 6 | 7 | **Check it out** my package in official gin contribute repository: [gin-gonic/contrib](https://github.com/gin-gonic/contrib) 8 | 9 | ### Feature 10 | - Simple rendering syntax for the template 11 | 12 | ```go 13 | // suppose "app/views/articles/list.html" is your file to be rendered 14 | c.HTML(http.StatusOK, "articles/list", "") 15 | ``` 16 | 17 | - Configure layout file 18 | - Configure template file extension 19 | - Configure templates directory 20 | - Feels friendlier for people coming from communities like rails, express or django. 21 | - **Template Helpers** ([gin_html_render.go](https://gist.github.com/madhums/4340cbeb36871e227905) is not support yet) 22 | 23 | #### Partials 24 | 25 | Supports rails style partials, simply name any template with underscore starting. So If you have "dashboard/index.tmpl" add "dashboard/_header.tmpl". Also there is a shared partial directory called "partials" under your template directory, 26 | anything you put there will be included with all templates. 27 | 28 | 29 | ###### Partials Example 30 | 31 | dashboard/_header.tmpl - define the template name 32 | 33 | ```go 34 | {{define "header"}} 35 |

Hi, This is Header

36 | {{ end }} 37 | ``` 38 | 39 | dashboard/index.tmpl - call partial template by it's name 40 | 41 | ```go 42 | {{define "content"}} 43 | Bla Bla Bla... 44 | {{template "header" .}} 45 | {{ end }} 46 | ``` 47 | 48 | ### How to use 49 | 50 | Suppose your structure is 51 | ```go 52 | |-- app/views/ 53 | |-- layouts/ 54 | |--- base.html 55 | |-- blogs/ 56 | |--- index.html 57 | |--- show.html 58 | 59 | See in "example" folder 60 | ``` 61 | 62 | ##### 1. Download package to your workspace 63 | ```go 64 | go get https://github.com/michelloworld/ez-gin-template 65 | ``` 66 | 67 | ##### 2. Import package to your application (*Import with alias) 68 | ```go 69 | import eztemplate "github.com/michelloworld/ez-gin-template" 70 | ``` 71 | 72 | ##### 3. Enjoy 73 | ```go 74 | r := gin.Default() 75 | 76 | render := eztemplate.New() 77 | 78 | // render.TemplatesDir = "app/views/" // default 79 | 80 | // render.Layout = "layouts/base" // default 81 | 82 | // render.Ext = ".html" // default 83 | 84 | // render.Debug = false // default 85 | 86 | 87 | // render.TemplateFuncMap = template.FuncMap{} 88 | 89 | r.HTMLRender = render.Init() 90 | r.Run(":9000") 91 | ``` 92 | 93 | ### Note 94 | 95 | I hope this package will resolve your problem about template system. 96 | and give you an idea about how to use **template helpers** in [Gin framework](https://github.com/gin-gonic/gin) 97 | 98 | Thanks, [multitemplate.go](https://github.com/gin-gonic/contrib/tree/master/renders/multitemplate), [gin_html_render.go](https://gist.github.com/madhums/4340cbeb36871e227905) for the idea 99 | -------------------------------------------------------------------------------- /ez-gin-template.go: -------------------------------------------------------------------------------- 1 | package eztemplate 2 | 3 | import ( 4 | "html/template" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/gin-gonic/gin/render" 11 | ) 12 | 13 | type Render struct { 14 | Templates map[string]*template.Template 15 | TemplatesDir string 16 | PartialDir string 17 | Layout string 18 | Ext string 19 | TemplateFuncMap map[string]interface{} 20 | Debug bool 21 | } 22 | 23 | func New() Render { 24 | r := Render{ 25 | 26 | Templates: map[string]*template.Template{}, 27 | // TemplatesDir holds the location of the templates 28 | TemplatesDir: "app/views/", 29 | // PartialDir holds the location of shared partials 30 | PartialDir: "partials/", 31 | // Layout is the file name of the layout file 32 | Layout: "layouts/base", 33 | // Ext is the file extension of the rendered templates 34 | Ext: ".html", 35 | // Template's function map 36 | TemplateFuncMap: nil, 37 | // Debug enables debug mode 38 | Debug: false, 39 | } 40 | 41 | return r 42 | } 43 | 44 | func (r Render) Init() Render { 45 | globalPartials := r.getGlobalPartials() 46 | 47 | layout := r.TemplatesDir + r.Layout + r.Ext 48 | 49 | viewDirs, _ := filepath.Glob(r.TemplatesDir + "**" + string(os.PathSeparator) + "*" + r.Ext) 50 | 51 | fullPartialDir := filepath.Join(r.TemplatesDir + r.PartialDir) 52 | for _, view := range viewDirs { 53 | templateFileName := filepath.Base(view) 54 | //skip partials 55 | if strings.Index(templateFileName, "_") != 0 && strings.Index(view, fullPartialDir) != 0 { 56 | localPartials := r.findPartials(filepath.Dir(view)) 57 | 58 | renderName := r.getRenderName(view) 59 | if r.Debug { 60 | log.Printf("[GIN-debug] %-6s %-25s --> %s\n", "LOAD", view, renderName) 61 | } 62 | allFiles := []string{layout, view} 63 | allFiles = append(allFiles, globalPartials...) 64 | allFiles = append(allFiles, localPartials...) 65 | r.AddFromFiles(renderName, allFiles...) 66 | } 67 | } 68 | 69 | return r 70 | } 71 | 72 | func (r Render) getGlobalPartials() []string { 73 | return r.findPartials(filepath.Join(r.TemplatesDir, r.PartialDir)) 74 | } 75 | 76 | func (r Render) findPartials(findPartialDir string) []string { 77 | files := []string{} 78 | path := filepath.Join(findPartialDir, "*"+r.Ext) 79 | partialDir, _ := filepath.Glob(path) 80 | for _, view := range partialDir { 81 | templateFileName := filepath.Base(view) 82 | //skip partials 83 | if strings.Index(templateFileName, "_") == 0 { 84 | renderName := r.getRenderName(view) 85 | if r.Debug { 86 | log.Printf("[GIN-debug] %-6s %-25s --> %s\n", "LOAD Partial", view, renderName) 87 | } 88 | 89 | files = append(files, view) 90 | } 91 | } 92 | return files 93 | } 94 | func (r Render) getRenderName(tpl string) string { 95 | dir, file := filepath.Split(tpl) 96 | dir = strings.Replace(dir, string(os.PathSeparator), "/", -1) 97 | tempdir := strings.Replace(r.TemplatesDir, string(os.PathSeparator), "/", -1) 98 | dir = strings.Replace(dir, tempdir, "", 1) 99 | file = strings.TrimSuffix(file, r.Ext) 100 | return dir + file 101 | } 102 | 103 | func (r Render) Add(name string, tmpl *template.Template) { 104 | if tmpl == nil { 105 | panic("template can not be nil") 106 | } 107 | if len(name) == 0 { 108 | panic("template name cannot be empty") 109 | } 110 | r.Templates[name] = tmpl 111 | } 112 | 113 | func (r Render) AddFromFiles(name string, files ...string) *template.Template { 114 | tmpl := template.Must(template.New(filepath.Base(r.Layout + r.Ext)).Funcs(r.TemplateFuncMap).ParseFiles(files...)) 115 | r.Add(name, tmpl) 116 | return tmpl 117 | } 118 | 119 | func (r Render) Instance(name string, data interface{}) render.Render { 120 | return render.HTML{ 121 | Template: r.Templates[name], 122 | Data: data, 123 | } 124 | } 125 | --------------------------------------------------------------------------------