@@ 129,6 129,31 @@ func (c *Context) Render(code int, name string, data interface{}) (err error) {
return c.HTMLBlob(code, buf.Bytes())
}
+// RenderTemplate is like Render except it accepts a *template.Template instance
+// to render from.
+func (c *Context) RenderTemplate(
+ tmpl *template.Template, code int, name string, data interface{}) (err error) {
+ if c.Server.e.Renderer == nil {
+ return echo.ErrRendererNotRegistered
+ }
+ vtmpl := c.Server.e.Renderer.(*validate.Template)
+ buf := new(bytes.Buffer)
+ if err = vtmpl.RenderTemplate(tmpl, buf, name, data, c); err != nil {
+ return
+ }
+ return c.HTMLBlob(code, buf.Bytes())
+}
+
+// NewTemplate will return a new template.Template instance based on the
+// current templateBase (see validate.Template)
+func (c *Context) NewTemplate(tfs fs.FS, filenames ...string) (*template.Template, error) {
+ if c.Server.e.Renderer == nil {
+ return nil, echo.ErrRendererNotRegistered
+ }
+ vtmpl := c.Server.e.Renderer.(*validate.Template)
+ return vtmpl.New(tfs, filenames...)
+}
+
// Error is needed because otherwise it will send an instance of
// *echo.context, and we want the user info, etc. from the gobwebs
// Context struct
@@ 9,9 9,6 @@ import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"netlandish.com/x/gobwebs"
-
- // Postgres
- _ "github.com/lib/pq"
)
type (
@@ 24,16 21,47 @@ type (
// Template defines the template registry struct
Template struct {
- templates *template.Template
+ templateBase *template.Template
+ templates *template.Template
staticMap Map
staticFuncs []func(c echo.Context) Map
+ cloned bool
}
)
// NewTemplate returns a *Template instance
func NewTemplate() *Template {
- return &Template{templates: template.New(""), staticMap: make(Map)}
+ return &Template{
+ templateBase: template.New(""),
+ templates: template.New(""),
+ staticMap: make(Map),
+ }
+}
+
+// New will return a clone of `t.templateBase` having
+// already parsed the given files. This is used when you don't want to use
+// the larger mix of already processed templates.
+func (t *Template) New(tfs fs.FS, filenames ...string) (*template.Template, error) {
+ tmpl, err := t.CloneBase()
+ if err != nil {
+ return nil, err
+ }
+ if tfs != nil {
+ tmpl, err = tmpl.ParseFS(tfs, filenames...)
+ } else {
+ tmpl, err = tmpl.ParseFiles(filenames...)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return tmpl, nil
+}
+
+// Clone will clone the `t.templateBase` Template to ensure all added static
+// funcs and variables are available
+func (t *Template) CloneBase() (*template.Template, error) {
+ return t.templateBase.Clone()
}
// Templates returns local *template.Template object
@@ 74,42 102,62 @@ func (t *Template) tmplPayload(c echo.Context, data Map) (Map, error) {
return input, nil
}
-// Render implements e.Renderer interface
-func (t *Template) Render(w io.Writer, name string, data any, c echo.Context) error {
+func (t *Template) render(
+ tmpl *template.Template, w io.Writer, name string, data any, c echo.Context) error {
// Add global methods if data is a map. Should be reached 99% of time
if viewContext, ok := data.(Map); ok {
input, err := t.tmplPayload(c, viewContext)
if err != nil {
return err
}
- return t.templates.ExecuteTemplate(w, name, input)
+ return tmpl.ExecuteTemplate(w, name, input)
}
- return t.templates.ExecuteTemplate(w, name, data)
+ return tmpl.ExecuteTemplate(w, name, data)
+}
+
+// Render implements e.Renderer interface
+func (t *Template) Render(w io.Writer, name string, data any, c echo.Context) error {
+ return t.render(t.templates, w, name, data, c)
+}
+
+// RenderTemplate takes a *template.Template object and populates it's
+// context with the static values already provided by `t`.
+func (t *Template) RenderTemplate(
+ tmpl *template.Template, w io.Writer, name string, data any, c echo.Context) error {
+ return t.render(tmpl, w, name, data, c)
}
// LoadTemplates parses templates given the glob pattern path
func (t *Template) LoadTemplates(path string, tfs fs.FS) error {
- // XXX Why do we have to use the tmp var here?
var (
- tmpt *template.Template
err error
+ tmpl *template.Template
)
+ if !t.cloned {
+ vtmpl, err := t.CloneBase()
+ if err != nil {
+ return err
+ }
+ t.templates = vtmpl
+ t.cloned = true
+ }
+
if tfs != nil {
- tmpt, err = t.templates.ParseFS(tfs, path)
+ tmpl, err = t.templates.ParseFS(tfs, path)
} else {
- tmpt, err = t.templates.ParseGlob(path)
+ tmpl, err = t.templates.ParseGlob(path)
}
if err != nil {
return err
}
- t.templates = tmpt
+ t.templates = tmpl
return nil
}
// AddFuncs adds template functions to the rendering template
func (t *Template) AddFuncs(funcs template.FuncMap) {
- t.templates = t.templates.Funcs(funcs)
+ t.templateBase.Funcs(funcs)
}
// AddStaticFunc adds a function to be called when building the default