Extensions for Giraffe to support development with htmx
Go to file
2024-12-13 17:02:28 -05:00
.github/workflows Update CI to Node 20-based versions 2024-02-12 18:29:27 -05:00
src Update version in READMEs 2024-12-13 17:02:28 -05:00
.gitignore Update script and version to 2.0.4; add .NET 9 support 2024-12-13 16:57:27 -05:00
LICENSE Initial commit 2021-10-03 21:53:36 -04:00
README.md Update for v2 beta 1 2024-03-18 20:03:18 -04:00

Giraffe.Htmx

Giraffe is a library that sits atop ASP.NET Core, and enables developers to create applications in a functional style (vs. the C# / object-oriented style of the base library). The Giraffe View Engine enables production of HTML views in a strongly-typed and fully-integrated-with-source fashion.

htmx is a library that embraces the idea of HTML as a programming language, where any element can fire off a request, and portions of the page can be swapped out dynamically. It does all these with a tiny, dependency-free JavaScript library.

htmx uses attributes and HTTP headers to attain its interactivity; the libraries here contain extensions to both Giraffe and Giraffe View Engine to enable strongly-typed development of htmx applications.

Installation

Giraffe.Htmx provides extensions that facilitate using htmx on the server side, primarily reading and setting headers. Giraffe.ViewEngine.Htmx provides attributes and helpers to produce views that utilize htmx. Both can be installed from NuGet via standard methods.

Server Side View Engine
Nuget Nuget

Server Side (Giraffe.Htmx)

In addition to the regular HTTP request payloads, htmx sets one or more headers along with the request. Once Giraffe.Htmx is opened, these are available as properties on HttpContext.Request.Headers. These consist of the header name, translated to a .NET name (ex. HX-Current-URL becomes HxCurrentUrl), and a strongly-typed property based on the expected value of that header. Additionally, they are all exposed as Options, as they may or may not be present for any given request.

A server may want to respond to a request that originated from htmx differently than a regular request. One way htmx can provide the same feel as a Single Page Application (SPA) is by swapping out the body content (or an element within it) instead of reloading the entire page. In this case, the developer can provide a partial layout to be used for these responses, while returning the full page for regular requests. The IsHtmx property makes this easy...

// "partial" and "full" are handlers that return the contents;
// "view" can be whatever your view engine needs for the body of the page
let result view : HttpHandler =
    fun next ctx ->
        if ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh then
            partial view
        else
            full view

htmx also utilizes response headers to affect client-side behavior. For each of these, this library provides HttpHandlers that can be chained along with the response. As an example, if the server returns a redirect response (301, 302, 303, 307), the XMLHttpRequest handler on the client will follow the redirection before htmx can do anything with it. To redirect to a new page, you would return an OK (200) response with an HX-Redirect header set in the response.

let theHandler : HttpHandler =
    fun next ctx ->
        // some interesting stuff
        withHxRedirect "/the-new-url" >=> Successful.OK

Of note is that the HX-Trigger headers can take either one or more events. For a single event with no parameters, use withHxTrigger; for a single event with parameters, or multiple events, use withHxTriggerMany. Both these have AfterSettle and AfterSwap versions as well.

View Engine (Giraffe.ViewEngine.Htmx)

As htmx uses attributes to extend HTML, the primary part of this library defines attributes that can be used within Giraffe views. Simply open Giraffe.ViewEngine.Htmx, and these attributes, along with support modules, will be visible.

As an example, creating a div that loads data once the HTML is rendered:

let autoload =
    div [ _hxGet "/lazy-load-data"; _hxTrigger HxTrigger.Load ] [
        str "Loading..."
    ]

(As hx-boost="true" is the usual desire for boosting, _hxBoost implies true. To disable it for an element, use _hxNoBoost instead.)

Some attributes have known values, such as hx-trigger and hx-swap; for these, there are modules with those values. For example, HxTrigger.Load could be used in the example above, to ensure that the known values are spelled correctly. hx-trigger can also take modifiers, such as an action that only responds to Ctrl+click. The HxTrigger module has a Filter submodule to assist with defining these actions.

let shiftClick =
    p [ _hxGet = "/something"; _hxTrigger (HxTrigger.Filter.Shift HxTrigger.Click) ] [
        str "hold down Shift and click me"
    ]

If you want to load htmx from unpkg, Htmx.Script.minified or Htmx.Script.unminified can be used to load the script in your HTML trees.

Feedback / Help

The author hangs out in the #dotnet-htmx channel (and most others) of the htmx Discord server and the #web channel of the F# Software Foundation's Slack server.

Thanks

Giraffe logo htmx logo JetBrains Logo (Main)
for making ASP.NET Core functional for making HTML cool again for licensing their tools to this project