Back to "En vissel Tour över HTMX-extensioner och hur man använder HT MX med ASP.NET Core"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Core HTMX Javascript

En vissel Tour över HTMX-extensioner och hur man använder HT MX med ASP.NET Core

Friday, 02 May 2025

Inleiding

HTMX är en kraftfull JavaScript bibliotek som tillåter dig att skapa dynamiska webbapplikationer med minimalt JavaScript.

Men jag säger fortfarande inte att jag har expertkunskaper om det. Jag ville bara dela med mig av några saker som jag lärt mig på vägen.

Kompanionsartikel: Den här artikeln fokuserar på HTMX's händelsesystem , utvecklingar , och avancerad personalisering M SK3 För ASP.NET Korallintegrationsmönster med partiell syn på systemet HTMX med ASP.

Erfarelser

Förberedels för begäran

Inställningsfasen är den där HTMX konfigurerar inställningen innan den skickas till servern.

flowchart LR

        A[htmx:configRequest] --> B[htmx:confirm] --> C[htmx:prompt] --> D[htmx:abort]

begäran om livscykel

Den efterfrågans livscykelfasen är där HTMX skickar in begäran till servern och hanterar svaret. De följande händelserna aktiveras under den här fasen:

flowchart LR
 E[htmx:beforeRequest] --> F[htmx:request] --> G[htmx:afterRequest]

Reaktionshantering

Response handlingsfasen är när HTMX bearbetar svaret från servern och uppdaterar dom.

flowchart LR
        H[htmx:beforeOnLoad] --> I[htmx:onLoad]
        I --> J[htmx:beforeSwap] --> K[htmx:swap] --> L[htmx:afterSwap]
        L --> M[htmx:afterSettle] --> N[htmx:afterOnLoad]

Historiska avdelningar

Historiens hanteringsfas är när HTMX uppdaterar webbläsarens historia och URL. De följande händelserna aktiveras under den här fasen:

flowchart LR
 A[htmx:historyRestoreRequest]
 A --> B[htmx:historyPopped]
 B --> C[htmx:historyRestorePage]

Som ni kan se så erbjuder HTMX ett antal händelser som du kan koppla in för att modifiera begäran eller svaret.,, eller till och med historien.;. HT MX gör ett LITT för att vara ett så kompaktt system.

Omlängningar

En av de kraftfullaste aspekterna av HTMX är förmågan att skapa förlängningar att utöka dess kapaciteter. I mitt fall htmx:configRequest att lägga till ytterligare parametrar till applikationen. Det är användbart när du vill skicka ytterligare data till servern utan att behöva modifiera HTML eller JavaScript-koden

Andra förlängningar kan hänga htmx:beforeRequest att ändra begäran innan den skickas efter de flesta andra förlängningar som hamnar configRequest; som i beforeRequest saker som HX-Vals och HX-Includes är redan kopplade till den vanligaste ( antingen i payloaden \ querystring Du kan till och med ha htmx:afterSwap att utföra saker efter att innehållet har bytts ut. Kombinerat med klientseitiga templeringsbibliotek som Alpine.js eller Lit du kan skapa kraftfulla dynamiska applikationer med minimal kod.

HTMX erbjuder några inbyggda extensioner som hx-boost och hx-swap-oob som tillåter dig att förbättra HTMXs funktionalitet utan att skriva någon egen kod.. Men det finns tillfällen då du behöver skapa dina egna förlängningar för att möta specifika krav.

Till exempel, kan du kanske vilja lägga till custom-Header till dina begäran , modifiera begäran-payloaden , eller hantera specifika händelser på ett unikt sätt

För att uppnå detta erbjuder HTMX några praktiska integreringspunkter:


{
  /**
   * init(api)
   * Called once when the extension is initialized.
   * Use it to set up internal state, store references, or access HTMX utility functions via the api parameter.
   */
  init: function(api) {
    return null;
  },

  /**
   * getSelectors()
   * Returns additional CSS selectors that HTMX should monitor.
   * Useful if your extension needs to handle custom elements or dynamic behavior.
   */
  getSelectors: function() {
    return null;
  },

  /**
   * onEvent(name, evt)
   * Called on every HTMX event (e.g., htmx:beforeRequest, htmx:afterSwap).
   * Return false to cancel the event or stop propagation.
   */
  onEvent: function(name, evt) {
    return true;
  },

  /**
   * transformResponse(text, xhr, elt)
   * Modify the raw response text before it is parsed and swapped into the DOM.
   * Use this to sanitize or preprocess HTML.
   */
  transformResponse: function(text, xhr, elt) {
    return text;
  },

  /**
   * isInlineSwap(swapStyle)
   * Return true if your extension will handle this swap style manually.
   * This tells HTMX to skip default behavior.
   */
  isInlineSwap: function(swapStyle) {
    return false;
  },

  /**
   * handleSwap(swapStyle, target, fragment, settleInfo)
   * Perform custom DOM manipulation if you implement a custom swap style.
   * Return true to prevent HTMX's default swap.
   */
  handleSwap: function(swapStyle, target, fragment, settleInfo) {
    return false;
  },

  /**
   * encodeParameters(xhr, parameters, elt)
   * Modify or serialize request parameters before sending.
   * Return null to use default URL/form encoding.
   * Return a string to override with a custom payload (e.g., JSON).
   */
  encodeParameters: function(xhr, parameters, elt) {
    return null;
  }
}

HTMX erbjuder ett antal pre- byggda förlängningar som du kan läsa om här.

Till exempel en praktisk byggt-in uitbreiding json-encode möjliggör att skicka JSON-data i begäranbasen istället för URL-koderade formdata. Det är användbart när du vill skicka komplexa datastrukturer eller rutnät till servernM SK2 Ni kan se att detta kopplas till 3 händelser.

  • init - för att sätta upp Erweiterungen och lagra en referens till HTMX API
  • onEvent - för att sätta Content-Type header till application/json när begäran konfigureras
  • encodeParameters - för att överrida den standardbaserade URL-koderade formkodering och serialisera parametrerna som JSONM SK2 Den återvänder också en ström för att förhindra HTMX från att använda den standardgjorda URL -koderad formkodingMSC4
(function() {
  let api
  htmx.defineExtension('json-enc', {
    init: function(apiRef) {
      api = apiRef
    },

    onEvent: function(name, evt) {
      if (name === 'htmx:configRequest') {
        evt.detail.headers['Content-Type'] = 'application/json'
      }
    },

    encodeParameters: function(xhr, parameters, elt) {
      xhr.overrideMimeType('text/json')

      const object = {}
      parameters.forEach(function(value, key) {
        if (Object.hasOwn(object, key)) {
          if (!Array.isArray(object[key])) {
            object[key] = [object[key]]
          }
          object[key].push(value)
        } else {
          object[key] = value
        }
      })

      const vals = api.getExpressionVars(elt)
      Object.keys(object).forEach(function(key) {
        // FormData encodes values as strings, restore hx-vals/hx-vars with their initial types
        object[key] = Object.hasOwn(vals, key) ? vals[key] : object[key]
      })

      return (JSON.stringify(object))
    }
  })
})()

Eller till och med den enklare men ännu mer praktiska hx-debug sträckning som lägger till en HX-Debug header till begäran. Det är användbart för avbugs- och loginsändningar, eftersom det tillåter dig att se råda begäran och reaktionsdata i devkonsolet

(function() {
  htmx.defineExtension('debug', {
    onEvent: function(name, evt) {
      if (console.debug) {
        console.debug(name, evt)
      } else if (console) {
        console.log('DEBUG:', name, evt)
      } else {
        throw new Error('NO CONSOLE SUPPORTED')
      }
    }
  })
})()

Det finns många fler än "'", med en väldigt kraftfull client side templating extension som låter dig använda klient bibliotek för att omvandla JSON data till HTML. Detta är användbart för att skapa dynamiska gränssnitt utan att behöva lita på servern.

Några anpassade förlängningar

Dynamiska Ry IDs

Till exempel i ett nytt projekt använde jag HTMX OOB-svap för att uppdatera ett antal rader i en tabell. För att göra detta ville jag veta vilka rader som nu visas i tabellen, så jag uppdaterade bara raderna som var synliga.

Extensionen

export default {
    encodeParameters: function (xhr, parameters, elt) {
        const ext = elt.getAttribute('hx-ext') || '';
        if (!ext.split(',').map(e => e.trim()).includes('dynamic-rowids')) {
            return null; // Use default behavior
        }

        const id = elt.dataset.id;
        const approve = elt.dataset.approve === 'true';
        const minimal = elt.dataset.minimal === 'true';
        const single = elt.dataset.single === 'true';

        const target = elt.dataset.target;
        const payload = { id, approve, minimal, single };

        if (approve && target) {
            const table = document.querySelector(target);
            if (table) {
                const rowIds = Array.from(table.querySelectorAll('tr[id^="row-"]'))
                    .map(row => row.id.replace('row-', ''));
                payload.rowIds = rowIds;
            }
        }

        // Merge payload into the parameters object
        Object.assign(parameters, payload);
        return null; // Return null to continue with default URL-encoded form encoding
    }
}

Att använda den

För att använda den behöver vi lägga till den till vår HTMX konfiguration. Så i din ingångspunkt js-lêer ( antagligen att du ' använder moduler; det borde vara

import dynamicRowIds from "./dynamicRowIds"; // Load the file

htmx.defineExtension("dynamic-rowids", dynamicRowIds); // Register the extension

På vilket element du än vill använda den kan du lägga till hx-ext attribut med värdet dynamic-rowids.

                <button
                    hx-ext="dynamic-rowids"
                    data-target="#my-table"
                    data-id="@Model.Id"
                    data-param1="true"
                    data-param2="false"
                    data-param3="@Model.Whatever"
                    hx-post
                    hx-controller="Contoller"
                    hx-action="Action"
                >
                    <i class='bx bx-check text-xl text-white'></i>
                </button>

Bevara parametrar

Det här är en annan enkel HTMX-extension htmx:configRequest som vi' modifierar URL: n innan rörelsen skickas. Den här utlängningen är tillförlitlig om du använder querystring-baserat filtering, etc.

Detta är SIMILAR till men inte exakt samma som den existerande HTMX-extensionen push-params

Extensionen

Ni kan se att vi onEvent att lyssna på htmx:configRequest händelse.

Så vi:

  • få elementet som utlöste händelsen
  • Ta den preserve-params-exclude attribut från elementet ( om det existerar) och delar det i en rad punkter för att utesluta ( så vi inte adderar dem till begäran
  • få den nuvarande URL-parameterna från vensterlokalen
  • få de nya parametrerna från formuläret
  • Loopa genom den nuvarande parametern och kolla om de inte är i den uteslutande listan och inte redan i de nya parametrerna
  • Om de inte är, så lägger vi dem till de nya parametrerna
  • Slutligen, satte vi de nya parametrerna till rörelsens URL och gav tillbaka true för att fortsätta med rörelsen
export default {
    onEvent: function (name, evt) {
        if (name !== 'htmx:configRequest') return;
        const el = evt.detail.elt;
        const excludeStr = el.getAttribute('preserve-params-exclude') || '';
        const exclude = excludeStr.split(',').map(s => s.trim());

        const currentParams = new URLSearchParams(window.location.search);
        const newParams = new URLSearchParams(evt.detail.path.split('?')[1] || '');

        currentParams.forEach((value, key) => {
            if (!exclude.includes(key) && !newParams.has(key)) {
                newParams.set(key, value);
            }
        });

        evt.detail.path = evt.detail.path.split('?')[0] + '?' + newParams.toString();

        return true;
    }
};

Här använder jag HTMX.Net för dess tag hjälpprogram. De liknande hx-controller och hx-action är tagbekymmer som genererar rätta HTMX-Attribute för dig. såväl som hx-route-<x> för värden att passera i vägen. Det är väldigt användbart eftersom det låter dig använda C# kod för att generera korrekta värden för attributen istället för att behöva hårdkoda dem i HTML

Att använda den

Eftersom det är en förlängning så är det väldigt enkelt att använda

Först måste vi lägga till den till vår HTMX konfiguration.


import preserveParams from './preserveParams.js';
htmx.defineExtension('preserve-params', preserveParams);

lägg märke till att standard HTMX-extensioner använder metoden 'autoload' för att ladda extensionen

// Autoloading the extension and registering it
(function() {
  htmx.defineExtension('debug', {
}

Det är ett bra sätt att göra det om du använder HTMX i en icke-modulmiljö. import uttalande för att ladda utlängningen och sedan uttryckligen registrera den mot din htmx instance. Det här tillåter dig att dra nytta av trädet -shaking och bara ladda de utlängningar du behöver

Sedan kan du lägga till elementet hx-ext attribut med värdet preserve-params och preserve-params-exclude attribut med en komma-separerad lista av parametrer som ska uteslutas från begäran.


<a class="btn-outline-icon"
   hx-controller="MyController"
   hx-action="MyAction"
   hx-route-myparam="@MyParam"
   hx-push-url="true"
   hx-ext="preserve-params"
   preserve-params-exclude="page,sort"
   hx-target="#page-content"
   hx-swap="innerHTML show:top"
   hx-indicator>
    <i class="bx bx-search"></i>
</a>

I det här fallet event.detail.path att ha den nya myparam värde sätter den i den ' kommer ersätta den där med vår nya värde men bevara alla andra ( förutom page och sort). Så vi kan fortsätta skicka alla filter som vi har satt in i URL till servern utan att behöva oroa oss för att de förloras när vi gör en ny begäran

ASP.NET Core

En av de häftiga sakerna med HTMX är att stor del av dess interaktion med servern sker genom HTTP-header. Dessa header ger servern en rik kontext om vad som upplöste begäran , och låter dig svara på det lämpligt från din ASP

Återigen en nyckelkomponent av detta är HTMX.Net. Bland många av sakerna som den erbjuder är prydliga Request utlängningar för att upptäcka HTMX- begäran. Det är användbart för att avgöra om en begäran gjordes av HT MX eller inte , och för att hantera den på det sättet

Det har också sin egen mekanisme för att skicka utlösande signaler


Response.Htmx(h => {
    h.WithTrigger("yes")
     .WithTrigger("cool", timing: HtmxTriggerTiming.AfterSettle)
     .WithTrigger("neat", new { valueForFrontEnd= 42, status= "Done!" }, timing: HtmxTriggerTiming.AfterSwap);
});

Push Urls etc...etc... Khalid gjorde ett fantastiskt jobb med att skapa en uppsättning Erweiterunger för att göra det enkelt att jobba med HTMX i ASP

Det är ett nyckelverktyg i min verktygslåda när jag arbetar med HTMX och ASP Kolla ut det!

Den vanliga HTMX-ansökande opskrifte

Här är en ventilation av de viktigaste opskriften som HTMX skickar med varje begäran.

Header Description
HX
HX
HX
HX
HX
HX
HX

Tillåta Erweiterunger

Jag använder dessa ganska mycket i mina ASP-apps Request.IsHtmx() , Request.IsHtmxBoosted() och Request.IsHtmxNonBoosted() du kan enkelt upptäcka HTMX-behov och svara accordingly.

Till exempel, Jag har en väldigt enkel Erweiterung till Beställning som låter mig upptäcka om en begäran är riktad mot mitt huvud #page-content div. Om det är så vet jag att jag borde skicka tillbaka en del NOTA: Många inser inte att man kan definiera en 'full sida ' som en del

        if (Request.PageContentTarget())
        {   
            Response.PushUrl(Request);
            return PartialView("List", vm);
        }
        return View("List", vm);
        
        public static class RequestExtensions
{
        
        public static bool PageContentTarget(this HttpRequest request)
    {
        bool isPageContentTarget = request.Headers.TryGetValue("hx-target", out var pageContentHeader) 
                                   && pageContentHeader == "page-content";
        
        return isPageContentTarget;
    }
    }

Reaktionslängder

Förutom förlängningar till begäran, kan du också skapa förlängelser till respons för att skicka händelser tillbaka till klienten.

Ett exempel på SweetAlert

Till exempel i min SweetAlert2 integration Jag gör det möjligt att stänga dialogen med hjälp av en aktivator som ställs från servern.

    document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);

Detta aktiveras från servern som en HTMX-utlösande händelse.


    public static void CloseSweetAlert(this HttpResponse response)
    {
        response.Headers.Append("HX-Trigger" , JsonSerializer.Serialize(new
        {
            sweetalert = "close"
        }));

    }

Det här kommer att aktivera sweetalert:close händelse på klientsidan, som tillåter dig att stänga dialogen. Du kan också skicka tillbaka data till klienten med hjälp av HX-Trigger header. Det här är användbart för att skicka data från servern till klienten utan att behöva modifiera HTML eller JavaScript kod

Som ni ser, är det väldigt lätt att lyssna på dessa händelser genom att bara lägga till en händelse lyssnare i kroppen.

ShowToast

Jag skrev tidigare om min toastmetod här, men det är det som är värt att nämna här också . För att mycket enkelt kunna få servern att signalera en toast notification på klientens sida, ställde jag ut aktiveringen i den här reaktionsextensionen

    public static void ShowToast(this HttpResponse response, string message, bool success = true)
    {
        response.Headers.Append("HX-Trigger", JsonSerializer.Serialize(new
        {
            showToast = new
            {
                toast = message,
                issuccess =success
            }
        }));

    }

Sedan kopplar jag in mig i eventclientsidan och ringer till min showToast funktion.

import { showToast, showHTMXToast } from './toast';

window.showHTMXToast = showHTMXToast;

document.body.addEventListener("showToast", showHTMXToast);

Det här låter mig showToast funktion och vält , visar en toast; se mer om det igen i artikeln .



export function showHTMXToast(event) {
    const xhr = event?.detail?.xhr;
    let type = 'success';
    let message = xhr?.responseText || 'Done!';

    try {
        const data = xhr ? JSON.parse(xhr.responseText) : event.detail;

        if (data.toast) message = data.toast;
        if ('issuccess' in data) {
            type = data.issuccess === false ? 'error' : 'success';
        } else if (xhr?.status >= 400) {
            type = 'error';
        } else if (xhr?.status >= 300) {
            type = 'warning';
        }

    } catch {
        if (xhr?.status >= 400) type = 'error';
        else if (xhr?.status >= 300) type = 'warning';
    }

    showToast(message, 3000, type);
}

I slutsats

Ja, det är det.

Kompanionsartikel

Detta artikel är en del av en serie med två delar på HTMX med ASP

  1. HTMX med ASP.NET Corepartial - fokuserar på ASP. Net Core integrering , partiell syn på , HTMX
  2. Denna artikel - djupt dyka in i HTMX-eventen , livscykeln , utbyggnadsarkitekturen

Fler HTMX-artiklar

Fortsättning avläsning

Officiell HTMX Dokumentation:

Bibliotek & verktyg:

Samhällens resurser:

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.