Search code examples
gogo-html-template

How to render templates for multiple models with common partials


I have many models in my golang project with CRUD views and I want to render these with common headers and footers but can't figure out how to do it. The examples I've seen are too simplistic.

Suppose I have a template structure like this:

templates
  - layouts
    - header.tmpl
    - footer.tmpl
  - users
    - index.tmpl
    - new.tmpl
    - edit.tmpl
    - show.tmpl   
  - venues
    - index.tmpl
    - new.tmpl
    - edit.tmpl
    - show.tmpl   

How do I render these templates for a specified model with the common header and footer?


Solution

  • just a barebones solution would be the following:

    package main
    
    import (
        "fmt"
        "os"
        "text/template"
    )
    
    func main() {
        //read in one go the header, footer and all your other tmpls.
        //append to that slice every time the relevant content that you want rendered.
        alltmpls := []string{"./layouts/header.tmpl", "./layouts/footer.tmpl", "./users/index.tmpl"}
        templates, err := template.ParseFiles(alltmpls...)
        t := templates.Lookup("header.tmpl")
        t.ExecuteTemplate(os.Stdout, "header", nil)
        t = templates.Lookup("index.tmpl")
        t.ExecuteTemplate(os.Stdout, "index", nil)
        t = templates.Lookup("footer.tmpl")
        t.ExecuteTemplate(os.Stdout, "footer", nil)
    }
    

    in reality you would want a function that returns a slice of the appropriate files to populate the alltmpls variable. It should scan your directories and get all files from there to pass to ParseFiles() and then proceed to call the Lookup and ExecuteTemplate steps for every template.

    Taking this idea further, i would create a new type that would embed a template (or a slice of templates) to be annotated by a header and a footer.

    type hftemplate struct {
        template.Template
        header, footer *template.Template
    }
    
    func (h *hftemplate) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
        h.header.ExecuteTemplate(wr, "header", nil)
        err := h.ExecuteTemplate(wr, name, data)
        h.footer.ExecuteTemplate(wr, "footer", nil)
        return err
    }
    

    and of course you can turn that struct embedding into a fully fledged field of []Template to do multiple ExecuteTemplates between the header and the footer.