├── .gitignore
├── config.json
├── templates
├── layouts
│ ├── js.tmpl
│ ├── style.tmpl
│ └── base.tmpl
├── index.tmpl
├── aboutme.tmpl
└── skillset.tmpl
├── README.md
├── pkg
└── windows_amd64
│ └── github.com
│ └── oxtoacart
│ └── bpool.a
└── src
├── main.go
└── templmanager
└── templatemanager.go
/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "LayoutPath": "templates/layouts/",
3 | "IncludePath": "templates/",
4 | "Port": 8080
5 | }
--------------------------------------------------------------------------------
/templates/layouts/js.tmpl:
--------------------------------------------------------------------------------
1 | {{ define "js" }}
2 |
5 | {{ end }}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Working example of https://medium.com/@asitdhal/golang-template-2-template-composition-and-how-to-organize-template-files-4cb40bcdf8f6#.mfny53p5w
--------------------------------------------------------------------------------
/pkg/windows_amd64/github.com/oxtoacart/bpool.a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asit-dhal/golang-template-layout/HEAD/pkg/windows_amd64/github.com/oxtoacart/bpool.a
--------------------------------------------------------------------------------
/templates/layouts/style.tmpl:
--------------------------------------------------------------------------------
1 | {{define "style"}}
2 |
12 | {{end}}
--------------------------------------------------------------------------------
/templates/index.tmpl:
--------------------------------------------------------------------------------
1 | {{define "title"}}Home{{end}}
2 | {{define "content"}}
3 |
7 | {{end}}
--------------------------------------------------------------------------------
/templates/aboutme.tmpl:
--------------------------------------------------------------------------------
1 | {{define "title"}}About Me{{end}}
2 | {{define "content"}}
3 | This is About me page.
4 |
5 | - My name is {{ .Name }}.
6 | - My home city is {{ .City }}.
7 | - My nationaliy is {{ .Nationality }}.
8 |
9 | Home
10 | {{end}}
--------------------------------------------------------------------------------
/templates/layouts/base.tmpl:
--------------------------------------------------------------------------------
1 | {{ define "base" }}
2 |
3 |
4 | {{block "title" .}} {{end}}
5 | {{block "style" .}} {{end}}
6 |
7 |
8 |
9 | {{template "content" .}}
10 |
11 |
12 | {{block "js" .}} {{end}}
13 |
14 |
15 |
16 | {{ end }}
17 |
--------------------------------------------------------------------------------
/templates/skillset.tmpl:
--------------------------------------------------------------------------------
1 | {{define "title"}}Skillset{{end}}
2 | {{define "content"}}
3 | My Skillset
4 |
5 |
6 |
7 | | Language |
8 | Level |
9 |
10 |
11 |
12 | {{ range . }}
13 |
14 | | {{ .Language }} |
15 | {{ .Level }} |
16 |
17 | {{ end }}
18 |
19 |
20 | Home
21 | {{end}}
--------------------------------------------------------------------------------
/src/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 | "os"
8 | "templmanager"
9 | )
10 |
11 | type UserData struct {
12 | Name string
13 | City string
14 | Nationality string
15 | }
16 |
17 | type SkillSet struct {
18 | Language string
19 | Level string
20 | }
21 |
22 | type SkillSets []*SkillSet
23 |
24 | type Configuration struct {
25 | LayoutPath string
26 | IncludePath string
27 | }
28 |
29 | func loadConfiguration(fileName string) {
30 | file, _ := os.Open(fileName)
31 | decoder := json.NewDecoder(file)
32 | configuration := Configuration{}
33 | err := decoder.Decode(&configuration)
34 | if err != nil {
35 | log.Println("error:", err)
36 | }
37 | log.Println("layout path: ", configuration.LayoutPath)
38 | log.Println("include path: ", configuration.IncludePath)
39 | templmanager.SetTemplateConfig(configuration.LayoutPath, configuration.IncludePath)
40 | }
41 |
42 | func index(w http.ResponseWriter, r *http.Request) {
43 | err := templmanager.RenderTemplate(w, "index.tmpl", nil)
44 | if err != nil {
45 | log.Println(err)
46 | }
47 | }
48 |
49 | func aboutMe(w http.ResponseWriter, r *http.Request) {
50 | userData := &UserData{Name: "Asit Dhal", City: "Bhubaneswar", Nationality: "Indian"}
51 | err := templmanager.RenderTemplate(w, "aboutme.tmpl", userData)
52 | if err != nil {
53 | log.Println(err)
54 | }
55 | }
56 |
57 | func skillSet(w http.ResponseWriter, r *http.Request) {
58 | skillSets := SkillSets{&SkillSet{Language: "Golang", Level: "Beginner"},
59 | &SkillSet{Language: "C++", Level: "Advanced"},
60 | &SkillSet{Language: "Python", Level: "Advanced"}}
61 | err := templmanager.RenderTemplate(w, "skillset.tmpl", skillSets)
62 | if err != nil {
63 | log.Println(err)
64 | }
65 | }
66 |
67 | func main() {
68 | loadConfiguration("config.json")
69 | templmanager.LoadTemplates()
70 |
71 | server := http.Server{
72 | Addr: "127.0.0.1:8080",
73 | }
74 |
75 | http.HandleFunc("/", index)
76 | http.HandleFunc("/aboutme", aboutMe)
77 | http.HandleFunc("/skillset", skillSet)
78 | log.Println("Listening ...")
79 | server.ListenAndServe()
80 | }
81 |
--------------------------------------------------------------------------------
/src/templmanager/templatemanager.go:
--------------------------------------------------------------------------------
1 | package templmanager
2 |
3 | import (
4 | "fmt"
5 | "github.com/oxtoacart/bpool"
6 | "html/template"
7 | "log"
8 | "net/http"
9 | "path/filepath"
10 | )
11 |
12 | var templates map[string]*template.Template
13 | var bufpool *bpool.BufferPool
14 | var mainTmpl = `{{define "main" }} {{ template "base" . }} {{ end }}`
15 |
16 | // create a buffer pool
17 | func init() {
18 | bufpool = bpool.NewBufferPool(64)
19 | log.Println("buffer allocation successful")
20 | }
21 |
22 | type TemplateConfig struct {
23 | TemplateLayoutPath string
24 | TemplateIncludePath string
25 | }
26 |
27 | type TemplateError struct {
28 | s string
29 | }
30 |
31 | func (e *TemplateError) Error() string {
32 | return e.s
33 | }
34 |
35 | func NewError(text string) error {
36 | return &TemplateError{text}
37 | }
38 |
39 | var templateConfig *TemplateConfig
40 |
41 | func SetTemplateConfig(layoutPath, includePath string) {
42 | templateConfig = &TemplateConfig{layoutPath, includePath}
43 | }
44 |
45 | func LoadTemplates() (err error) {
46 |
47 | if templateConfig == nil {
48 | err = NewError("TemplateConfig not initialized")
49 | return err
50 | }
51 | if templates == nil {
52 | templates = make(map[string]*template.Template)
53 | }
54 |
55 | layoutFiles, err := filepath.Glob(templateConfig.TemplateLayoutPath + "*.tmpl")
56 | if err != nil {
57 | return err
58 | }
59 |
60 | includeFiles, err := filepath.Glob(templateConfig.TemplateIncludePath + "*.tmpl")
61 | if err != nil {
62 | return err
63 | }
64 |
65 | mainTemplate := template.New("main")
66 |
67 | mainTemplate, err = mainTemplate.Parse(mainTmpl)
68 | if err != nil {
69 | log.Fatal(err)
70 | }
71 | for _, file := range includeFiles {
72 | fileName := filepath.Base(file)
73 | files := append(layoutFiles, file)
74 | templates[fileName], err = mainTemplate.Clone()
75 | if err != nil {
76 | return err
77 | }
78 | templates[fileName] = template.Must(templates[fileName].ParseFiles(files...))
79 | }
80 |
81 | log.Println("templates loading successful")
82 | return nil
83 |
84 | }
85 |
86 | func RenderTemplate(w http.ResponseWriter, name string, data interface{}) error {
87 | tmpl, ok := templates[name]
88 | if !ok {
89 | http.Error(w, fmt.Sprintf("The template %s does not exist.", name),
90 | http.StatusInternalServerError)
91 | err := NewError("Template doesn't exist")
92 | return err
93 | }
94 |
95 | buf := bufpool.Get()
96 | defer bufpool.Put(buf)
97 |
98 | err := tmpl.Execute(buf, data)
99 | if err != nil {
100 | http.Error(w, err.Error(), http.StatusInternalServerError)
101 | err := NewError("Template execution failed")
102 | return err
103 | }
104 |
105 | w.Header().Set("Content-Type", "text/html; charset=utf-8")
106 | buf.WriteTo(w)
107 | return nil
108 | }
109 |
--------------------------------------------------------------------------------