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

Handle hx-boost requests according to hx-target #26

Closed egil closed 2 months ago

egil commented 2 months ago
          What is the mechanism for targeting that HtmxPartial component?

Normal request or hx-boosted request

During a normal request or hx-boosted request, the full component tree is rendered, i.e. starting with App.razor, all the way down to the Counter.razor page.

This is potentially incorrect. On an hx-boosted request the rendered output can still target a particular selector. The documentation doesn't give you much to indicate it but hx-target is still adhered to along with hx-swap as well. Even if hx-target is body then hx-boosted requests only replace the body of the current page without changing the url (used for non-GET requests). If hx-target is "window" or empty (?) then it's a full page request.

For example:

<navbar>
    <a href="/home" hx-swap="innerHTML transition:true scroll:main:top show:none" hx-boost="true" hx-target="#main_content">Home</a>
</navbar>

<main>
    <div id="main_content"></div>
</main>

This would load the content at url /home as boosted, swapping it into #main_content with a transition, immediately setting the scrollbar to the top of main without any animation, and overriding any default show scroll animations. This would allow you to load content into a particular div just as you would with hx-get.

As such, a full page request to /home should render normally and a boosted page request to /home is the same as any other hx-get.

I see you added something more so I'm going to read that before adding anything further.

Originally posted by @tanczosm in https://github.com/egil/Htmxor/issues/25#issuecomment-2088507621

egil commented 2 months ago

I have added a new internal property:

https://github.com/egil/Htmxor/blob/3829d5c213b60ab8db022a9911ae2fa458bc9f7e/src/Htmxor/Http/HtmxRequest.cs#L12

It will be used by the library to determine whether or not to do a full page render or targeted component only render, and whether or not to apply targeted fragment/partial logic as well.

tanczosm commented 2 months ago

This needs to be adjusted as follows, as the target defaults to the document.body if Target is unset. Also in htmx you can't directly target body using hx-target="body" as it doesn't have an id attribute (see getHeaders method in htmx source), but because it defaults to body anyway it still works so checking for an empty Target is sufficient.

internal bool IsFullPageRequest => !IsHtmxRequest || (IsBoosted && string.isNullOrWhiteSpace(Target));

boosted requests aren't always full page requests if a Target is present. On requests that are boosted, though, the htmx code below is used to retrieve the target for swapping the element. What you can see is that if hx-target is present the target will then be based on evaluating the hx-target string first.

If hx-target isn't specified and data.boosted is present then you can see the default target is getDocument().body. getDocument() just returns document, so this is just document.body as the target as default.

        function getTarget(elt) {
            var targetStr = getClosestAttributeValue(elt, "hx-target");
            if (targetStr) {
                if (targetStr === "this") {
                    return findThisElement(elt,'hx-target');
                } else {
                    return querySelectorExt(elt, targetStr)
                }
            } else {
                var data = getInternalData(elt);
                if (data.boosted) {
                    return getDocument().body;
                } else {
                    return elt;
                }
            }
        }
egil commented 2 months ago

Thanks, that makes sense to me. Lets go with that.