├── 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 |
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 |
--------------------------------------------------------------------------------