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

Proper Usage of `<HtmxPartial>` #27

Closed sregdorr closed 2 months ago

sregdorr commented 2 months ago

Hello @egil! I love what you're doing here! I have no doubt this will be an amazing contribution to the Blazor ecosystem!

I was trying to learn the ideas/concepts behind this library and even though I have started utilizing htmx in a few of my projects, reading through your code samples was the first time I had run across the idea of template-fragments. I love it! that's so much more concise than having to deal with directories of component partials.

That being said, I noticed some weird behavior regarding your <HtmxPartial> component. Riffing off of your ./samples/MinimalHtmxorApp/Components/Pages/Counter.razor component, I was playing with this test component to try to see how you would use multiple <HtmxPartial>'s in a single component:

@page "/counter"
@using Htmxor
<PageTitle>Counter</PageTitle>

<h1>Counter</h1>
<p>
    This is a htmx enabled counter. It uses the <code>&lt;HtmxPartial&gt;</code> to
    enable the concept <a href="https://htmx.org/essays/template-fragments/" target="_blank">template fragments</a>.
</p>
<p>
    When Htmxor sees a <code>&lt;HtmxPartial&gt;</code> component, it will only render it's content, and not the surrounding
    content, like these paragraph elements.
</p>

<div hx-target="this" id="counter">
    <HtmxPartial>
        <p role="status">
            Current count: @CurrentCount
        </p>
        <button class="btn btn-primary"
                hx-put="/counter"
                hx-vals='{ "Inc": true, "CurrentCount": @(CurrentCount) }'
                @onput="UpdateCount">
            Click to increment

        </button>
    </HtmxPartial>
</div>
<div hx-target="this" id="test">
    <HtmxPartial>
        <div hx-get="/counter?SayHello=true&ReloadCount=0" hx-swap="outerHTML" hx-trigger="load delay:5s">
            @if (SayHello)
            {
                <p>Hello!!</p>
                <p>@ReloadCount</p>
            }
            else
            {
                <p>Loading...</p>
            }
        </div>
    </HtmxPartial>
</div>

@code {
    [SupplyParameterFromQuery] private bool SayHello { get; set; }
    [SupplyParameterFromQuery] private int ReloadCount { get; set; } = -1;
    [SupplyParameterFromForm] private bool Inc { get; set; }
    [SupplyParameterFromForm] private int CurrentCount { get; set; } = 0;

    protected override void OnInitialized()
    {
        ReloadCount++;
    }

    private void UpdateCount(HtmxEventArgs args)
    {
        // Access the HtmxContextEventArgs to control
        // the response headers to the client.
        // E.g.: args.Response.StatusCode(201);

        CurrentCount = Inc ? CurrentCount + 1 : CurrentCount - 1;
    }
}

Looking at the code, I was expecting that on a counter button click, I would get a response from the server that patched simply the div#counter element (which is exactly what happens), and after 5 seconds when the div#test triggers, it would be patched with the contents of the second <HtmxPartial>. However, it actually gets patched with the content of the counter partial:

image

image

Am I misunderstanding how the <HtmxPartial> component is supposed to work? Is it not intended to be used multiple times within a single component? Is there some way of naming/identifying the individual instances such that the Renderer can tell the difference when patching the DOM?

Thanks again for your work on this! I'm excited to follow your progress!

egil commented 2 months ago

Thanks for the kind words. You are not missing something, we are actively working on this. Check out https://github.com/egil/Htmxor/issues/25 and add your input there. Ill close this issue as the other one covers this topic.