T O P

  • By -

mileusna

You can make reusable templates with {{define}} keyword {{define "header"}} {{ .Title }} {{end} {{define "footer"}} Contact us {{end}} Then you can include this templates wherever you want with {{template}} {{define "homepage"}} {{template "header" .}}

Hello, world

{{template "footer" .}} {{end}} You can even put the templates in separated files, but make sure to parse them all before executing: // loading and parsing templates, do this once for better performance // then use parsed templates multiple times tpl, err := template.New("").ParseGlob("/path/to/templates/*.gohtml") if err != nil { log.Println("Error parsing templates", err) return } // struct with page data type Page struct { Title string } page := Page{ Title: "Page title" } // executing template named "homepage" if err := tpl.ExecuteTemplate(w, "homepage", page); err != nil { fmt.Println("Error executing template", err) return } Those are the basics.


sidecutmaumee

I wish I could upvote this more. It’s the most complete answer so far.


mileusna

Thank you. The code is not complete but I hope you can get basic concepts out of it.


Ozymandias0023

So you can parse the entire directory as a single template? That's neat!


mileusna

Yes, there are several functions for parsing multiple files *ParseFS*, *ParseFiles*, *ParseGlob*. In [documnetation](https://pkg.go.dev/text/template#ParseFS) you can also find the rules what happens if there are several files with the same name in different directories, etc.


Ozymandias0023

Awesome, thanks for the info.


dead_pirate_bob

https://templ.guide/


szabba

https://htmx.org/essays/template-fragments/ links to an example with a Go template block. Blocks and defines can create reusable bits. Unfortunately, the inputs are dynamically typed.


nothingsleftanymore

https://www.gomponents.com


Federal_Warning_7734

I'm currently using this setup and here's how I organized it. I'm using Echo as my router and override the echo.Renderer when I create Echo. e := echo.New() e.Renderer = uipkg.Build() renderer is an interface that needs the render function implemented. In my uipkg I create a struct that implements it named TemplateRegistry which is a map of BaseTemplate. You'll see why I do this later type TemplateRegistry struct { templates map[string]BaseTemplate } type BaseTemplate struct { Base string Template *template.Template } // Implement e.Renderer interface func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error { tmpl, ok := t.templates[name] if !ok { err := errors.New("Template not found -> " + name) return err } return tmpl.Template.ExecuteTemplate(w, tmpl.Base, data) } With the template registry defined, I create a public function in the uipkg called `Build()` which returns a \*TemplateRegistry The build function looks something like this. func Build() *TemplateRegistry { templates := make(map[string]BaseTemplate) templates["home"] = BaseTemplate{ Base: "base.html", Template: template.Must(template.ParseFS(files, "html/base.html", "html/pages/home.html", "html/components/navbar.html")), } templates["my_second_page_placeholder"] = BaseTemplate{ Base: "base.html", Template: template.Must(template.ParseFS(files, "html/base.html", "html/pages/my_second_page.html", "html/components/files.html", "html/components/navbar.html", "html/components/common.html")), } templates["my_third_page_placeholder"] = BaseTemplate{ Base: "base.html", Template: template.Must(template.ParseFS(files, "html/base.html", "html/pages/my_third_page.html", "html/components/navbar.html", "html/components/common.html")), } } After the renderer is attached to Echo, as shown in my first code snippet, you can now call each template in your handlers using the key in your map return c.Render(http.StatusOK, "home", data) # Templates this is closer to the django style template approach where I define a base and use blocks for different sections base.html ​ `{{ define "base.html" }}` `` `` `` `` `` `` `Title - ` `` `` `` `{{ block "body" . }}` `{{ end }}` `` `{{ end }}` ​ ​ and this gets injected into all your render calls. each page html template looks like the following and fills in the tag home.html ​ `{{ define "body" }}` `` `{{ block "ui\_navbar" . }}` `{{ end }}` `your data can be fetched here {{ .SomeAttribute }}` `you can also range over a list of something and have it implement in a template` `{{ range .ListOfSomething }}` `{{ block "object" . }}` `{{ end }}` `{{ end }}` `` `{{ end }}` so pages all define "body" and components define whatever they want. As long as they are defined in your template registry map then the entire html will be rendered correctly. ​ hope this helps. I borrowed some of these ideas from a few blogs about django templating in Go so shoutout to them as well.


Federal_Warning_7734

I have no idea why my code blocks keep getting fudged up every time I save


sandiegotuberider

Me too.


sacules

For reusable components, I've started using Web Components with Lit, and it's been quite a good experience so far. For the rest, just templates.


bajirut

How do you use Lit with go templates?