egil / Htmxor

Supercharges Blazor static server side rendering (SSR) by seamlessly integrating the Htmx.org frontend library.
MIT License
109 stars 12 forks source link

HtmxSwappable component ideas #32

Closed tanczosm closed 2 months ago

tanczosm commented 2 months ago

I added an HtmxSwappable component of sorts but it's in a pretty rough state. I wrote over the weather example for the test since it already used lazy loading as a pattern.

There are quite a few issues to work out. The url isn't set when it's a normal page request so lazy loading won't work. Also, if I click weather in the navbar then it does an infinite recursive reload of the weather page rather than targeting just that component.

To be fair, this was an extremely quick workup so there is a lot to consider (such as when to know it's OOB or not). I just wanted to submit a draft PR to see if any of this is workable.

egil commented 2 months ago

Yeah, I am concerned about the conditional render logic being too "magic" and not obvious to the end user, and more granular rules may need to be applied than just "this is an hx request and I have a child component that implements IConditionalOutputComponent, so then I wont output anything by default".

Ill experiment a bit myself and report back here.

tanczosm commented 2 months ago

Magic is fine if you can layer on top of it a few components that solve common use cases.

To be fair I didn't focus at all on the rendering logic using Condition but I'm not sure how to turn off all the other rendering just to target one element. I was trying to put together the core of the component.

In the original render fragment document they provided a mechanism to individually target a fragment for rendering but I'm not immediately sure how to do that. In the example calls they used #fragment when calling the renderer. Not sure how that would translate to routable urls.

On Fri, May 3, 2024, 6:10 AM Egil Hansen @.***> wrote:

Yeah, I am concerned about the conditional render logic being too "magic" and not obvious to the end user, and more granular rules may need to be applied than just "this is an hx request and I have a child component that implements IConditionalOutputComponent, so then I wont output anything by default".

Ill experiment a bit myself and report back here.

— Reply to this email directly, view it on GitHub https://github.com/egil/Htmxor/pull/32#issuecomment-2092704759, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAUHLRC3T6KYIY26MOFNKPTZANPDBAVCNFSM6AAAAABHEYLIAKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJSG4YDINZVHE . You are receiving this because you authored the thread.Message ID: @.***>

egil commented 2 months ago

Pushed some changes to main that changes the implicit magic to opt in magic via either manually implementing ICondititionalOutputComponent or inheriting from ConditionalComponentBase that does the magic. I like that much better.

egil commented 2 months ago

I think I see where you are heading with this.

I've been thinking about what kind of first-party components that make sense. Here is where I am at right now:

For "Htmx" components like HtmxSwappable to add value, they have to enable scenarios that users could not achieve (easily) using just HTML elements themselves.

Take the the weather-data example in the PR. What value does the user gain by using <HtmxSwappable>:

<HtmxSwappable TargetId="weather_status" SwapStyle="SwapStyle.InnerHTML" Selector=".weather-message">
    @if (forecasts.Length > 0)
    {
        <span>Loaded weather for @forecasts.First().Date.ToShortDateString() to @forecasts.Last().Date.ToShortDateString()</span>
    }
</HtmxSwappable>

vs just going native htmx like this:

<div id="weather_status" hx-swap-oob="InnerHTML:.weather-message">
    @if (forecasts.Length > 0)
    {
        <span>Loaded weather for @forecasts.First().Date.ToShortDateString() to @forecasts.Last().Date.ToShortDateString()</span>
    }
</div >

Unless I am missing something here, I do not see value. You can say the same about HtmxLazy.

For me, Htmxor is about making Blazor work well with Htmx, that is, making it easy to do things you want in a backend to service an Htmx-enabled frontend. It's not about hiding away htmx by creating custom components. Creating html elements with attributes is very native to Blazor after all.

I don't want to discourage you, I appreciate all your experience and help!

tanczosm commented 2 months ago

I'm a little discouraged, but not for your reasons because I agree that I don't want to varnish over top of htmx without good cause. I'm just not able to express the end goal which would be to not have to worry about deciding if something is an OOB swap or not.

If components on a page can decide to output themselves or not, unless I am misunderstanding the render changes you made it's possible that any number of components could elect to output themselves in a request.

However, it will all just get rendered together into some target on request unless you opt for each of the multiple rendered fragments to swap in via oob swaps. Ideally you could see if there was a way to hide away the need to decide if something required an oob swap or not and make that more transparent if possible.

Frequently when working with single partials you are either rendering them inline with page content or if an update is needed pushing them out as an OOB swap. I've used a service where you just queue exactly the components that need to be swapped in and render out all the OOB swaps at the end of the request.

tanczosm commented 2 months ago

I agree about HtmxLazy at this point. I would close that PR for now but if you could keep the branch for a bit it's helpful to my understanding of your renderer changes.

As an aside with your example, on an htmx request what you wrote won't do anything if the corresponding selector doesn't already exist in the dom. Even without the selector, it wouldn't do anything on an htmx request if the matching id element isn't already in the dom. Though to be fair, this is an undocumented htmx feature (the best kind). On a true non-ajax full page request it will render because it isn't processed by htmx. So how would oob swaps even work with a page of template fragments?

egil commented 2 months ago

It seems clear that I need to write some documentation about how the routing and rendering works. Then that will hopefully clear things up for you and everybody else, because as far as I can tell, your concern isn't a problem. It may turn out that I am completely wrong about that and my understanding of htmx is fundamentally wrong though 😊

What would be very helpful is if you could describe scenarios you think are valuable and how Htmxor ideally should enable them, i.e. what markup should be generated during a full-page request, during a htmx-request, and how you would like to write the Blazor component that generates said markup.