Closed odrotbohm closed 2 years ago
I am trying to understand how to exactly use this. I want to write a variant of the TodoMvc app I've build with htmx before. That one uses triggers to do an extra request to the server to update the item count on the page when an item is added or removed. I'd like to use OOB swaps to test the API this PR brings.
I currently have this:
@PostMapping
@HxRequest
public String htmxAddTodoItem(TodoItemFormData formData,
Model model) {
TodoItem item = repository.save(new TodoItem(formData.getTitle(), false));
model.addAttribute("item", toDto(item));
return "fragments :: todoItem";
}
I want to now return that same fragment + fragments :: active-items-count
fragment as OOB. The fragment needs a single attribute numberOfActiveItems
to be present to render properly.
I got started like this:
return new HtmxPartials()
.replace("active-items-count")
.with("fragments :: active-items-count");
1) Where do I set the extra attribute (numberOfActiveItems
)? Just put it on the injected Model
instance? It would have been great if I could re-use the method public String htmxActiveItemsCount(Model model)
I already had. Pseudo-code:
return new HtmxPartials().replace("active-items-count").with(this::htmxActiveItemsCount);
It could take the result String
as the fragment name and it would need to somehow pass in the model. Not sure if this is even possible, but just wanted to put this out there to think about.
2) Where do I add the "main content" fragment? I need a way to also have the fragments :: todoItem
returned.
@odrotbohm Thanks a ton for this contribution here are my findings, which I'm happy to tackle and contribute to your branch (if you don't mind)
Out-of-band changes are typically added to a base change (note the div without an hx-swap-oob
attribute:
<div id="alert" hx-swap-oob="true">User successfully updated</div>
<div>
<span>User details</div>
</div>
There are a few partials that I expect will get updated frequently. Allowing the user to create helper functions for those partials would be helpful:
class MyHtmxPartials extend HtmxPartials {
public void addAlert(String alertText) {
// add partial and model the allows for alerting
}
}
The current partial are only appended based on the return type. Adding an annotation and model attribute will allow alternate options
https://htmx.org/attributes/hx-swap-oob/ See:
any valid [hx-swap](https://htmx.org/attributes/hx-swap) value, followed by a colon, followed by a CSS selector
I'd be also fine if you took this over into a feature branch in the main repo and take it from there.
What's the story behind the extensions? How are those helper functions supposed to work? Also, can you elaborate "Allow not returning an HtmxPartial"? That's nothing that needs explicit support as it works OOTB, doesn't it? Spring MVC renders fragments already if the view name template::fragment
is returned.
Allow not returning an HtmxPartial
I mean to be able to have a ModelAndView
as the the return value for a method, but still have the HtmxPartial
processing to kick in. I'm not certain how useful it is. More just wondering if requiring the return value to trigger the processing is the best approach.
This isn't a requirement, just wanted to ensure it wasn't too fragile if a different approach is needed.
Extension
I was just trying to think of how a user might extend the partials, like the 'alert' in my example. The helper function would just bundle calls for setting the model and selecting the replace/template calls. I'll add a test that demonstrates it (and if it is useful)
I've switched the target branch from 'main' to feature/partials
so I can iterate on it a little more. Thanks so much for the contribution @odrotbohm !
Introduce HtmxPartials to provide a convenient API for controller methods to specify Thymeleaf fragments to be rendered to ultimately trigger different parts of the page updated. The abstraction is handled in a custom HandlerInterceptor that renders a view that triggers the rendering of the registered fragments individually.
I've stripped down a lot of stuff from what I previously had in Spring Playground. The server sent events support is completely gone for now as that allows a significantly simplified arrangement and less abstractions to introduce. Feel free to massage as you see fit.