Extensions for Giraffe to support development with htmx
Go to file
Daniel J. Summers 9efac866ed Implement HX-Reswap header (#5)
- Rework project structure to add common project
2022-07-14 08:52:47 -04:00
src Implement HX-Reswap header (#5) 2022-07-14 08:52:47 -04:00
.gitignore Initial Development (#1) 2021-11-02 19:07:16 -04:00
LICENSE Initial commit 2021-10-03 21:53:36 -04:00
README.md Add scripts to README 2022-01-06 21:31:36 -05: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 ->
      match ctx.Request.IsHtmx && not ctx.Request.IsHtmxRefresh with
      | true -> partial view
      | false -> 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 "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 #htmx-general channel 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