Skip to content

Commit

Permalink
add a way to pass functions to templates. make the wrapper nested so …
Browse files Browse the repository at this point in the history
…that we can load a partials that is further nested.
  • Loading branch information
donseba committed Sep 24, 2024
1 parent 44bf039 commit 29085c3
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 18 deletions.
69 changes: 67 additions & 2 deletions component.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,24 @@ import (

var (
DefaultTemplateFuncs = template.FuncMap{}
templateCache = sync.Map{} // Cache for parsed templates
UseTemplateCache = true
templateCache = sync.Map{} // Cache for parsed templates
)

type (
RenderableComponent interface {
Render(ctx context.Context) (template.HTML, error)
Wrap(renderer RenderableComponent, target string) RenderableComponent
With(r RenderableComponent, target string) RenderableComponent
Attach(target string) RenderableComponent
SetData(input map[string]any) RenderableComponent
AddData(key string, value any) RenderableComponent
SetGlobalData(input map[string]any) RenderableComponent
AddGlobalData(key string, value any) RenderableComponent

AddTemplateFunction(name string, function interface{}) RenderableComponent
AddTemplateFunctions(funcs template.FuncMap) RenderableComponent

SetURL(url *url.URL)

data() map[string]any
Expand All @@ -36,16 +44,19 @@ type (
templateData map[string]any
with map[string]RenderableComponent
partial map[string]any
globalData map[string]any
wrappedRenderer RenderableComponent
wrappedTarget string
templates []string
url *url.URL
functions template.FuncMap
}
)

func NewComponent(templates ...string) *Component {
return &Component{
templateData: make(map[string]any),
functions: make(template.FuncMap),
partial: make(map[string]any),
with: make(map[string]RenderableComponent),
templates: templates,
Expand Down Expand Up @@ -87,7 +98,13 @@ func (c *Component) renderNamed(ctx context.Context, name string, templates []st
// Cache template parsing
tmpl, cached := templateCache.Load(templates[0])
if !cached || !UseTemplateCache {
tmpl, err = template.New(name).Funcs(DefaultTemplateFuncs).ParseFiles(templates...)
var functions = DefaultTemplateFuncs
if c.functions != nil {
for key, value := range c.functions {
functions[key] = value
}
}
tmpl, err = template.New(name).Funcs(functions).ParseFiles(templates...)
if err != nil {
return "", err
}
Expand All @@ -97,11 +114,13 @@ func (c *Component) renderNamed(ctx context.Context, name string, templates []st
data := struct {
Ctx context.Context
Data map[string]any
Global map[string]any
Partials map[string]any
URL *url.URL
}{
Ctx: ctx,
Data: input,
Global: c.globalData,
Partials: c.partial,
URL: c.url,
}
Expand Down Expand Up @@ -130,13 +149,51 @@ func (c *Component) With(r RenderableComponent, target string) RenderableCompone
return c
}

// Attach adds a template to the main component but doesn't pre-render it
func (c *Component) Attach(target string) RenderableComponent {
c.templates = append(c.templates, target)
return c
}

func (c *Component) AddTemplateFunction(name string, function interface{}) RenderableComponent {
c.functions[name] = function

return c
}

func (c *Component) AddTemplateFunctions(funcs template.FuncMap) RenderableComponent {
for key, value := range funcs {
c.functions[key] = value
}

return c
}

func (c *Component) SetGlobalData(input map[string]any) RenderableComponent {
c.globalData = input

return c
}

func (c *Component) AddGlobalData(key string, value any) RenderableComponent {
c.globalData[key] = value

return c
}

// SetData adds data to the component
func (c *Component) SetData(input map[string]any) RenderableComponent {
c.templateData = input

return c
}

func (c *Component) AddData(key string, value any) RenderableComponent {
c.templateData[key] = value

return c
}

func (c *Component) SetURL(url *url.URL) {
c.url = url
}
Expand Down Expand Up @@ -170,6 +227,14 @@ func (c *Component) injectData(input map[string]any) {
}
}

func (c *Component) injectGlobalData(input map[string]any) {

Check failure on line 230 in component.go

View workflow job for this annotation

GitHub Actions / lint

func `(*Component).injectGlobalData` is unused (unused)
for key, value := range input {
if _, ok := c.globalData[key]; !ok {
c.globalData[key] = value
}
}
}

// addPartial adds a partial to the component
func (c *Component) addPartial(key string, value any) {
c.partial[key] = value
Expand Down
44 changes: 28 additions & 16 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,27 +269,39 @@ func (h *Handler) Render(ctx context.Context, r RenderableComponent) (int, error
return 0, err
}

// if it is a partial render, return the output directly
// If it's a partial render, return the output directly
if h.RenderPartial() {
return h.WriteHTML(output)
}

// if the component is wrapped, we need to render all the partials inside out
if r.isWrapped() {
parent := r.wrapper()
parent.SetURL(h.r.URL)
parent.injectData(r.data())
parent.addPartial(r.target(), output)

// inject the output into the parent template
pOutput, pErr := parent.Render(ctx)
if pErr != nil {
return 0, pErr
}
// render the parent template
return h.WriteHTML(pOutput)
// Recursively wrap the output if the component is wrapped
output, err = h.wrapOutput(ctx, r, output)
if err != nil {
return 0, err
}

// render the output if it is not a partial render and not wrapped
// Write the final output
return h.WriteHTML(output)
}

// wrapOutput recursively wraps the output in its parent components
func (h *Handler) wrapOutput(ctx context.Context, r RenderableComponent, output template.HTML) (template.HTML, error) {
if !r.isWrapped() {
// Base case: no more wrapping
return output, nil
}

parent := r.wrapper()
parent.SetURL(h.r.URL)
parent.injectData(r.data())
parent.addPartial(r.target(), output)

// Render the parent component
parentOutput, err := parent.Render(ctx)
if err != nil {
return "", err
}

// Recursively wrap the parent output if the parent is also wrapped
return h.wrapOutput(ctx, parent, parentOutput)
}
2 changes: 2 additions & 0 deletions sse/sse.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ func (m *Message) String() string {
}
sb.WriteString(fmt.Sprintf("data: %v\n\n", m.Data))

fmt.Println(sb.String())

return sb.String()
}

Expand Down

0 comments on commit 29085c3

Please sign in to comment.