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

Support hx-target-XXX ? #58

Open StefH opened 2 months ago

StefH commented 2 months ago

Hello Egil, do you have any plans to support this https://htmx.org/extensions/response-targets/ ?

egil commented 2 months ago

Hey @StefH,

Natively Htmxor will support what Htmx comes with out of the box. To support any extensions, Htmxor will probably need to be expanded with a generic HtmxResponse.Headers property, so developers can create their extension methods on HtmxResponse that set specific headers.

In this particular case, I am not sure Htmxor needs anything besides what it already has for the response-targets extension to work.

You can set the status code through HtmxResponse and just include the attributes you want like normal. Let me know if I am missing something.

StefH commented 2 months ago

Hi @egil,

I got this working in the demo app with:

:one:

Adding

builder.AddMarkupContent(2, @"<script defer src=""_content/Htmxor/htmx/response-targets.js""></script>");

to HtmxHeadOutlet.

Note that just adding the script in my main app:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="MinimalHtmxorApp.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HtmxHeadOutlet UseEmbeddedHtmx="false" />
    <script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/response-targets.js"></script>
    <HeadOutlet />
</head>

<body hx-boost="true">
    <Routes />
</body>

</html>

Did not work?

response-targets.js:76 Uncaught ReferenceError: htmx is not defined
    at response-targets.js:76:5
    at response-targets.js:130:3

:two: Update html + blazor code:

<div id="counter" hx-ext="response-targets"> ⭐
    <HtmxFragment>
        <p role="status">
            Current count: @CurrentCount
        </p>
        <button class="btn btn-primary"
                hx-put="/counter"
                hx-vals='{ "CurrentCount": @(CurrentCount) }'
                hx-target="#counter"
                hx-target-5*="#serious-errors" ⭐
                @onput="IncrementCount">
            Click me
        </button>
        <div id="serious-errors"></div> ⭐
    </HtmxFragment>
</div>

@code {
    [SupplyParameterFromForm]
    private int CurrentCount { get; set; } = 0;

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

        CurrentCount++;

        if (CurrentCount == 5)
        {
                // ⭐ Return a 500 status code to the client
            args.Response.StatusCode(HttpStatusCode.InternalServerError);
        }
    }
}

However, I actually want to return the exception message back.

Instead of the normal body:

<p role="status">
            Current count: 5</p>
        <button class="btn btn-primary" hx-put="/counter" hx-vals="{ &quot;CurrentCount&quot;: 5 }" hx-target="#counter" hx-target-5*="#serious-errors" hxor-eventid="0567AF4F">
            Click me
        </button>
        <div id="serious-errors"></div>

Is this scenario supported in Htmx?

egil commented 2 months ago

In this case you should not set <HtmxHeadOutlet UseEmbeddedHtmx="false" />. Only set UseEmbeddedHtmx="false" if you want to include your own version of htmx.js.

If you just want to use an additional htmx extensions, just include it after the HtmxHeadOutlet. Do note that HtmxHeadOutlet include scripts with the defer attribute set.

egil commented 2 months ago

Some statuscodes result in no content being sent, i.e. HttpStatusCode.NoContent, but otherwise, you should be able to set any statuscode you want and return any result.

I am still learning htmx though, so I am not entirely sure how it behaves by default with 500 status codes. It does have some default behaviors with 404 status code and others but your extension may override that.