Closed tanczosm closed 4 months ago
Did you consider using Blazors "sections concept" to do this? There are even custom section types like <HeadContent>
built into Blazor.
In an app, relevant certain parts of a page like a navbar component placed in a layout would provide a section outlet that pages and components can write into. On normal requests, that just works as is. On HX-requests, any <SectionContent>
components would be included in the response. It will require some tinkering the rendering engine which I plan to do anyway, but this should work.
Leaning on the section-concept is the idiomatic Blazor approach. Getting it to work is the challenge.
Let me know what you think.
Sections would be fine to determine the rendering point, but it should definitely be available as a service to push the components / content that will be rendered onto the page. The idea would be to expose it to other libraries and classes that might also need to inject content into a page without having actual access to the page itself.
Content could even be potentially rendered out-of-band on a regular page request. Alpine.js uses a css attribute called x-cloak that hides elements until they are fully on the page. A similar approach could work with htmxor to hide injected elements until htmx fully loads the page, since htmx will still process OOB swaps even if it's a normal page load not from htmx. That would take some client-side javascript as part of the htmxor framework to remove the cloaking attribute once the page is loaded but it's pretty simple.
Side note.. something like this made me think you could kind of reproduce streaming using OOB swaps. You could basically pre-render components and then awaiting the quiescence task. When that's complete you push the result into the service as an OOB swap and let htmx handle it. You'd still need to do some response buffer tricks though to pull that off, but an immediately htmx friendly-way would be to push swaps via websocket connections, SSE, or the htmx-signalr extension.
A more expansive approach would utilize something of a pub/sub model. Any approach you start with, there is definitely a lot of opportunity for swapped in content.
To understand your proposed solution, can you create some example code that shows a few of the different scenarios (don't implement it, just show how the API would work)?
I think this would probably require some discussion but on a base level users would need to be able to add components/content for publishing as OOB swaps.
If it's kept generic where all this content ultimately gets injected into a section outlet then there is no need to get complicated by specifically handling OOB swaps. Instead create a base set of razor components that mirror htmx principles. The name HtmxSwapService may not be appropriate then but I'm not sure what else I'd go with.
public interface IHtmxSwapService
{
// Method to add a Razor component with a parameter dictionary/object
void AddComponent<TComponent>(string target, SwapStyle swapStyle, string selector, Dictionary<string, object> parameters = null ) where TComponent : IComponent;
// Method to add a Razor component with RenderFragment as a parameter
void AddFragment(string target, SwapStyle swapStyle, string selector, RenderFragment renderFragment) where TComponent : IComponent;
// Add Raw html content
void AddContent (string target, SwapStyle swapStyle, string selector, string content)
// Render the list of components to a ??? type so that it is included in the output of the page
public RenderFragment RenderToFragment ();
}
Then maybe we create a Razor Component for swaps. It could either just render the OOB swap html or better, add the component content wrapped in a swappable div to the SwapService implementation for inclusion in the page at the end.
Something like:
<HxSwappable Id="alerts" SwapStyle="SwapStyle.OuterHTML" CssSelector="whatever">
... Content here
</HxSwappable>
A few thoughts on HxSwappable after putting it into Rizzy. It does work well as a defined OOB swap component but the swap service only works adding content outside of blazor page lifecycle methods. My hope was to be able to render swaps at the end of the response after the initial page render on htmx requests. Unfortunately blazor response rendering was completing before the SwapService contains any content if any of the add methods are called inside of a page.
Section outlets aren't really a solution because ideally you could piggyback and render swaps on any htmx request to get page content updates rather than what might be in a full page request.
Htmxor might work as you have better control over the renderer.
Interesting. Can you share an example that I can test against and perhaps your ideal solution?
I'll think about this a bit. For Rizzy it wasn't much of an issue to render swaps outside of the Razor components. I tested it primarily with page navigation that may need to be updated as you move from page to page without a full refresh. This could include elements like updating whether a user is logged in, their current notification count, and even full navigation bars if you were to visit a subsection of a larger site (say going from the main page to a Technology subsection that has a different navbar).
HxSwappable, as described above, can cover a lot of use cases btw. It can act as a container for content that can be swapped into (wrap a data table in HxSwappable, for example) or if rendered out of bounds on an htmx request as a vehicle to swap in content (rendering the next page of a data table as an OOB request).
Is this still relevant @tanczosm?
Let's close it for now.
Description:
Service Name: HtmxSwapService (Alternative names: OOBSwapService, ComponentSwapService)
Currently, there's a need for a mechanism to perform Out-of-Band (OOB) swaps in response to htmx requests. This feature request proposes the addition of a scoped service named HtmxSwapService to fulfill this requirement. While it is possible to add OOB swaps anywhere you want, they still need to be largely done on HTMX requests rather than full page renders.
A service would then allow you to easily inject the components / html you want to render on an out-of-band basis. It could also be utilized by third-party components as well to add interactivity to the page.
Htmx supports the following swaps:
Functionality:
Example Usage: