The go-partial package offers advanced features to handle dynamic content rendering based on user interactions or server-side logic. Below are three advanced use cases:
- WithSelection: Selecting partials based on a selection key.
- WithAction: Executing server-side actions during request processing.
- WithTemplateAction: Invoking actions from within templates.
WithSelection allows you to select and render one of several predefined partials based on a selection key, such as a header value or query parameter. This is useful for rendering different content based on user interaction, like tabbed interfaces.
Use WithSelection when you have a static set of partials and need to render one of them based on a simple key provided by the client.
Create partials for each selectable content.
tab1Partial := partial.New("tab1.gohtml").ID("tab1")
tab2Partial := partial.New("tab2.gohtml").ID("tab2")
defaultPartial := partial.New("default.gohtml").ID("default")
Map selection keys to their corresponding partials.
partialsMap := map[string]*partial.Partial{
"tab1": tab1Partial,
"tab2": tab2Partial,
"default": defaultPartial,
}
Use WithSelection to associate the selection map with your content partial.
contentPartial := partial.New("content.gohtml").ID("content").WithSelection("default", partialsMap)
In your content.gohtml template, use the {{selection}} function to render the selected partial.
<div class="content">
{{selection}}
</div>
In your handler, render the partial as usual.
func yourHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
err := contentPartial.WriteWithRequest(ctx, w, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Set the selection key via a header (e.g., X-Select) or another method.
<div hx-get="/your-endpoint" hx-headers='{"X-Select": "tab1"}'>
<!-- Content will be replaced with tab1 content -->
</div>
WithAction allows you to execute server-side logic during request processing, such as handling form submissions or performing business logic, and then render a partial based on the result.
Use WithAction when you need to perform dynamic operations before rendering, such as processing form data, updating a database, or any logic that isn't just selecting a predefined partial.
Attach an action function to the partial using WithAction.
formPartial := partial.New("form.gohtml").ID("contactForm").WithAction(func(ctx context.Context, data *partial.Data) (*partial.Partial, error) {
// Access form values
name := data.Request.FormValue("name")
email := data.Request.FormValue("email")
// Perform validation and business logic
if name == "" || email == "" {
errorPartial := partial.New("error.gohtml").AddData("Message", "Name and email are required.")
return errorPartial, nil
}
// Simulate saving to a database or performing an action
// ...
// Decide which partial to render next
successPartial := partial.New("success.gohtml").AddData("Name", name)
return successPartial, nil
})
In your handler, render the partial with the request.
func submitHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
err := formPartial.WriteWithRequest(ctx, w, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Submit the form with an X-Action header specifying the partial ID.
<form hx-post="/submit" hx-headers='{"X-Action": "contactForm"}'>
<input type="text" name="name" placeholder="Your Name">
<input type="email" name="email" placeholder="Your Email">
<button type="submit">Submit</button>
</form>
- The form submission triggers the server to execute the action associated with contactForm.
- The action processes the form data and returns a partial to render.
- The server responds with the rendered partial (e.g., a success message).
WithTemplateAction allows actions to be executed from within the template using a function like {{action "actionName"}}. This provides flexibility to execute actions conditionally or at specific points in your template.
Use WithTemplateAction when you need to invoke actions directly from the template, possibly under certain conditions, or when you have multiple actions within a single template.
Attach template actions to your partial.
myPartial := partial.New("mytemplate.gohtml").ID("myPartial").WithTemplateAction("loadDynamicContent", func(ctx context.Context, data *partial.Data) (*partial.Partial, error) {
// Load dynamic content
dynamicPartial := partial.New("dynamic.gohtml")
// Add data or perform operations
return dynamicPartial, nil
})
In your mytemplate.gohtml template, invoke the action using the action function.
<div>
<!-- Some content -->
{{action "loadDynamicContent"}}
<!-- More content -->
</div>
Render the partial as usual.
func yourHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
err := myPartial.WriteWithRequest(ctx, w, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
- Conditional Execution
{{if .Data.ShowSpecialContent}} {{action "loadSpecialContent"}} {{end}}
- Lazy Loading
<div hx-get="/loadContent" hx-trigger="revealed"> {{action "loadHeavyContent"}} </div>
- Actions are only executed when the {{action "actionName"}} function is called in the template.
- This allows for conditional or multiple action executions within the same template.
- The server-side action returns a partial to render, which is then included at that point in the template.
- Use
WithSelection
when you have a set of predefined partials and want to select one based on a simple key. - Use
WithAction
when you need to perform server-side logic during request processing and render a partial based on the result. - Use
WithTemplateAction
when you want to invoke actions directly from within the template, especially for conditional execution or multiple actions.
- Separation of Concerns: While WithTemplateAction provides flexibility, be cautious not to overload templates with business logic. Keep templates focused on presentation as much as possible.
- Error Handling: Ensure that your actions handle errors gracefully and that your templates can render appropriately even if an action fails.
- Thread Safety: If your application is concurrent, ensure that shared data is properly synchronized.