HTMX med ASP (Svenska (Swedish))

HTMX med ASP

Friday, 28 November 2025

//

15 minute read

Inleiding

Om du har byggt traditionella ASP, ., NET Core MVC-applikationer och , vet du vad problemet är. att känna modern.

Tekniken att återfå partiella HTML-fragment från servern och byta dem till DOM löser detta - och det är inte nytt' . Jag har använt det här mönstret sedan jQuerys dagar, , och även innan det med vanilla JavaScript och XMLHttpRequest. Vad elegant det' blir med HTMX

HTMX ger oss ett deklarativt sätt att göra det vi alltid gjort.', :, servern tillbaka.-, HTML som skickas fram och omvandlas till sidan.

Kompanionsartikel: Den här artikeln fokuserar på ASP-integrationen. En vissel Tour över HTMX-extensioner och hur man använder HT MX med ASP.NET Core.

I den här artikeln kommer jag att visa hur HTMX integreras vackert med ASP HTMX.NET bibliotek gör det ännu bättre mostlylucid.pagingtaghelper NuGet paketet använder HTMX och erbjuder kraftfull webbing med minimal konfiguration.

Ansatsen är framework, -, och också agnostisk. Django lade fram szablonfragment i versionen. Rails har Turborammar, ,, och det bredare web-ekosystemet omfamnar HTML.

Vad är HTMX?

HTMX är en bibliotek som låter dig få tillgång till moderna webblärafunktioner direkt från HTML, snarare än att skriva JavaScript. Det utvidger HTML med attribut som tillåter dig att göra AJAX- begäran , byta innehållet , och skapa rika interaktioner M SK4 allt utan att lämna ditt märke

De viktigaste attributen du kommer att använda

  • hx-get, hx-post, hx-put, hx-delete - Gör HTTP- begäran
  • hx-target - Specifiera var svaret ska placeras
  • hx-swap - Kontrollera hur innehållet skiftas innerHTML, outerHTML, etc
  • hx-trigger - Definiera vad som utlöser begäran. klick, , förändring,, ladda,,, etc.
  • hx-push-url - uppdatera webbläsarens URL utan att ladda upp hela sidan igen

Här är "'". Det är skönheten med det. : , du, ' , skriver fortfarande servern. - , sidakod, M SK4 , återvänder servern, - , skickar HTML.

Ett mönster lika gammalt som AJAX själv

Innan HTMX, uppnådde vi samma effekt med betydligt fler ceremonier . Här' är hur partiella uppdaterade händelser såg ut i jQuery- eran

// jQuery circa 2010
$('#load-more').click(function() {
    $.ajax({
        url: '/posts/page/' + currentPage,
        success: function(html) {
            $('#posts-container').append(html);
            currentPage++;
        }
    });
});

Och ännu tidigare, med vanilla JavaScript

// Vanilla JS circa 2005
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
        document.getElementById('content').innerHTML = xhr.responseText;
    }
};
xhr.open('GET', '/partial-content', true);
xhr.send();

Servern- var identisk med dess sida-mönster ♫ - ♫ återfå HTML fragment ♫ , ♫ byter dem till DOM ♫. ♫ HTMX flyttar bara den här logiken från JavaScript till HTML-attribute ♫ , ♫ gör det deklarativt ♫, ♫ upptäckbar ♫ Sk6 ♫ och långt mindre fel ♫ sk7 ♫ antaglig ♫ SMk8 ♫ Innovationen är inte ♫ ' ♫ tekniken ♫ Smk10 ♫ utan ♫ smk11 ♫ gränssnittet ♫

ASP.NET-utvecklare har gjort detta i åratal PartialView() i MVC sedan dag ett, Html.RenderAction() för sammankommningsbara fragment - möjligheten har alltid funnits där . Vad vi saknade var en elegant, standardiserad sätt att koppla upp den på kundensidanM SK3 HTMX fyller den klyftan perfektMSC4

Den bredare industrin har också återupptäckt dessa mönster.. Begrepp som: ", SSR, " och ( Server, -, sida- Rendering, Mska5, Mske6, hybrid- rendering, M Ska7 och Mska8, ISLANDs arkitektur, M ska9, beskriver i grunden vad server, M SK10, sida ramverk alltid har gjort.,, men med nya ögon.

Att installera HTMX i ASP.NET Core

Först, inkludera HTMX i din layout. Du kan använda en CDN eller servera den lokalt

<script src="https://unpkg.com/[email protected]"></script>

Det är det.

Alpine.js

För klienten- sida-reaktivitet ( att visa / gömda element| ,| radande tillstånd| ,| lokalt gränssnittステetet Alpine.js kompletterar HTMX perfekt

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

Här är hur de arbetar tillsammans.

<div x-data="{ open: false }">
    <button x-on:click="open = !open">Toggle</button>
    <div x-show="open" x-transition>
        <button hx-get="/api/data" hx-target="#results">Load Data</button>
    </div>
</div>

Alpine hanterar den lokala gränssnittet (, att byta, HTMX hanterar serveruppdrag, (, datalämpningen, )., Under hela artikeln, M SK4, ser du detta mönster. ♫ -, Alpine för klientreaktiviteter, MSC7, Htmx för serverinteraktioner,

Biblioteket HTMX.NET

Khalid Abuhakmeh's HTMX.NET bibliotek tillhandahåller den första ASP-klassen, - Htmx och Htmx.TagHelpers NuGet packages, känns inhemskt till .NET och gör det till ett absolut nöje att jobba med HTMX. GitHub.

Installation

dotnet add package Htmx
dotnet add package Htmx.TagHelpers

I din _ViewImports.cshtml:

@addTagHelper *, Htmx.TagHelpers

IsHtmx() Erweiterungsmetoden

Det mest användbara är Request.IsHtmx() förlängningsmetoden, som säger till dig om begäran kom från HTMX

[HttpGet]
public async Task<IActionResult> Index(int page = 1, int pageSize = 20)
{
    var posts = await blogViewService.GetPagedPosts(page, pageSize);

    if (Request.IsHtmx())
        return PartialView("_BlogSummaryList", posts);

    return View("Index", posts);
}

Detta mönster är absolut lysande. En enda kontrollaktion fungerar för båda

  • Hela sidan laddas ( när användare navigerar direkt eller uppdaterar
  • Litegranna uppdaterade versioner ( när HTMX gör begäran)

Inga separata API- slutpunkter , inga duplicerade logiker, ingen JSON serialisering överhuvudtaget

Ett vanligt missuppfattning: Många ASP.NET-utvecklare tror att du behöver _ prefix (lik _BlogSummaryList.cshtml) för att få en partiell rendering . Du gör inte PartialView() metoden själv säger till ASP.NET Core att hoppa över layouten - det ' som säger ♫ " glöm layoutet ♫ return PartialView("SearchResults", model) med en vanlig bildfiler och det fungerar perfekt. underscore-konventionen uppstod från ASP.NET webbsidor (WebMatrixM SK3 där den förhindrade att archive serverades direkt via URL MSC4 men MVC har alltid skyddat alla bilder från direkt tillgång hursomhelst avsedda som delar.

HTMX.NET Etikettbekymmer

HTMX.NET erbjuder tagbekymmer som gör att arbetet med kontroller är renare . Istället för att skriva ut spårsträngar , kan du använda starkt

<button
    hx-controller="Comment"
    hx-action="GetCommentForm"
    hx-post
    hx-target="#commentform">
    Reply
</button>

Detta genererar den korrekta vägen med hjälp av ASP.NET Core'riktningssystemet

Här är ett verkligt exempel från den här bloggen

<button
    class="btn btn-outline btn-sm mb-4"
    hx-action="Comment"
    hx-controller="Comment"
    hx-post
    hx-vals
    x-on:click.prevent="window.mostlylucid.comments.setValues($event)"
    hx-on="htmx:afterSwap: window.scrollTo({top: 0, behavior: 'smooth'})"
    hx-swap="outerHTML"
    hx-target="#commentform">
    Comment
</button>

Titta på hur HTMX spelar fint med Alpine.js (x-on:click.prevent) för de då och dåliga bitarna av klienten- sidainteraktivitetM SK2

Andra HTMX.NET hjälparbetare

Biblioteket erbjuder också

  • Request.IsHtmxNonBoosted() - Kolla om det är en HTMX- begäran men inte upphöjt
  • Request.IsHtmxRefresh() - Kolla om det är en historia återställelse begäran
  • Redaktions hjälpmedel för HTMX-kolor ( triglar , redirectioner, etcM SK3

Verklig-World Example: söka med delar

Här är sökreglern från den här bloggen.

[HttpGet]
[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request", "pagerequest" })]
public async Task<IActionResult> Search(
    string? query,
    int page = 1,
    int pageSize = 10,
    [FromHeader] bool pagerequest = false)
{
    var searchModel = await BuildSearchModel(query, page, pageSize);

    if (pagerequest && Request.IsHtmx())
        return PartialView("_SearchResultsPartial", searchModel.SearchResults);

    if (Request.IsHtmx())
        return PartialView("SearchResults", searchModel);

    return View("SearchResults", searchModel);
}

Tre återvändsvägar för tre scenarior:

  1. Seitenupplägg - minimalt delaktigt | ( | bara listan av resultat |)
  2. Filterförändringar - sektioner delaktiga | ( | resultat med filter |)
  3. Direktnavigation - hela sidan

Det delaktiga synsättet (_SearchResultsPartial.cshtml) använder tagassistenten

@model Mostlylucid.Models.Blog.PostListViewModel
<div class="pt-2" id="content">
    @if (Model.Data?.Any() is true)
    {
        <div class="inline-flex w-full items-center justify-center pb-4">
            @if (Model.TotalItems > Model.PageSize)
            {
                <pager
                    x-ref="pager"
                    link-url="@Model.LinkUrl"
                    hx-boost="true"
                    hx-target="#content"
                    hx-swap="show:none"
                    page="@Model.Page"
                    page-size="@Model.PageSize"
                    total-items="@Model.TotalItems"
                    hx-headers='{"pagerequest": "true"}'>
                </pager>
            }
        </div>
        @foreach (var post in Model.Data)
        {
            <partial name="_ListPost" model="post"/>
        }
    }
</div>

Att bryta ner pagertag hjälparen:

  • hx-boost="true" - Intercepterar länkar , konverterar till AJAX
  • hx-target="#content" - Var man ska injicera reaktionen
  • hx-headers='{"pagerequest": "true"}' - Custom-Header säger till kontrollören att det är 's pagination
  • Controllern kontrollerar Request.IsHtmx() && pagerequest för att återfå bara den minimala deldelen

Det mest tydliga packaget.pagingtaghelper

Jag skrev mostlylucid.pagingtaghelper för att undvika repetitiv pagination kod. Det's HTMXMSC2first men fungerar utan JavaScript ocksåM SK3

Installation

dotnet add package mostlylucid.pagingtaghelper

Lägg till _ViewImports.cshtml:

@addTagHelper *, mostlylucid.pagingtaghelper

Nyckliga egenskaper

Implementera IPagingModel<T> och du

public class BasePagingModel<T> : IPagingModel<T> where T : class
{
    public int Page { get; set; }
    public int TotalItems { get; set; }
    public int PageSize { get; set; }
    public string LinkUrl { get; set; }
    public List<T> Data { get; set; }
}

Vad får du:

  • Null konfiguration krävs
  • Flera gränssnitt (TailwindCSS | + | DaisyUI | , | Bootstrap | 5, | custom views
  • Dark mode support
  • 8 språk byggde
  • Sortierbara opskrifte, sidans storlekslekar
  • Fortskridande förbättring ( fungerar utan JavaScript
  • Fortsättningstoken-stöd för NoSQL-databaser

Etiketteassistenten genererar länkar som bevarar söksträngar, stödjer räätällig nagłówk, och integrerar genomskärt med HTMX ( se exemplet ovanförM SK3

HTMX-flödesdiagramm

Här är hur hela systemet passar ihop.

graph TB
    A[Browser] -->|"Initial page load"| B[Controller Action]
    B -->|"Request.IsHtmx() = false"| C[Return Full View]
    C --> D[Render Layout + Partial]

    A -->|"User clicks pagination/filter"| E[HTMX Request]
    E -->|"hx-get with headers"| F[Same Controller Action]
    F -->|"Request.IsHtmx() = true"| G{Check Headers}
    G -->|"pagerequest header"| H[Return Minimal Partial]
    G -->|"No special header"| I[Return Section Partial]

    H --> J[Swap Content in Target]
    I --> J

    J -->|"User clicks another link"| E

    style B stroke:#333,stroke-width:2px
    style F stroke:#333,stroke-width:2px
    style C stroke:#0066cc,stroke-width:2px
    style H stroke:#00cc66,stroke-width:2px
    style I stroke:#00cc66,stroke-width:2px

jämförelse med andra ramverk

Django-templatefragment

Django lade till med rätt delversions-template-rendering i versionen 6.0 (December | 2024) | med templatefragmenter |. | Innan det ♫ , ♫ använde Django utvecklare vanligtvis inkluderande tag eller tredjedelar ♫ PartialView() sedan versionen 1.0 i ♫ 2016 ♫ ♫ МSK2 ♫ olika ramverk ♫ , ♫ flera tidsramar ♫ m, ♫ men samma destination ♫ M: ♫ HTML fragment för HTMX ♫

Rörlar Turborammar

Ruby on Rails har turboramar ( en del av Hotwire), som är lik i själenM SK2

<%= turbo_frame_tag "posts" do %>
  <%= render @posts %>
<% end %>

Skillnaden är att Turbo kräver specifika rammarkörer både på begäran och på svaren. hx-target.

Phoenix LiveView

Elixir's Phoenix LiveView tar ett annat tillvägagångssätt med bestående WebSocket-kopplingar och server

def handle_event("load_more", _params, socket) do
  {:noreply, assign(socket, posts: load_more_posts())}
end

LiveView är lysande för riktiga applikationer.

Tillvägagångspunkter

Input Caching: Den OutputCache attribut varierar med hx-request header, kakande av hela sidor och delar separat

[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request", "pagerequest" })]

nätverkseffektivitet: Server

Bundle-storlek: HTMX (14KB) | | + | optional Alpine | . | js | {\displaystyle |} (15 | Kb | МSK6 | _ |+ | pgging tag helper | (eng.) (0 | kb ♫ , | server | [eng.] - | sida | LMB 11 | lMB 12 | under 30 | Total | Łäng |

avancerade mönster

Optimistiska gränssnittuppdateringar - Kombinera HTMX och Alpine för direkt återkoppling

<div x-data="{ count: @Model.CommentCount }">
    <button hx-post="/comment/like" x-on:click="count++" hx-on::after-request="count = $event.detail.xhr.response">
        Likes: <span x-text="count"></span>
    </button>
</div>

Beräkningen uppdateras omedelbart (optimistiskt ), och sinkroniseras sedan med serveruppkopplingen

Utan bandsvapningar - uppdatera flera sidor från ett svar

<div id="main-content"><!-- Main response --></div>
<div id="notification-count" hx-swap-oob="true"><span>5 new</span></div>

Perfekt för budskapsblöcke, Körkolor , etc. Jag täcker detta mönster djupt i Att visa bröd och bytes med HTMX.

Client-Side-template med WebAPI - Ibland vill du ha en mellantids- -upplevelse med : servern, -- -som ger HTML för de flesta sakerna, ,- -men JSON från ett WebAPI för specifik dynamisk innehåll. klient-sidan-templates förlängning låter dig göra exakt detta

<script src="https://unpkg.com/[email protected]/client-side-templates.js"></script>
<script src="https://unpkg.com/mustache@latest"></script>

<div hx-ext="client-side-templates">
    <template id="post-template" type="text/mustache">
        {{#posts}}
        <div class="post">
            <h3>{{title}}</h3>
            <p>{{excerpt}}</p>
        </div>
        {{/posts}}
    </template>

    <button hx-get="/api/posts"
            hx-target="#post-list"
            mustache-template="post-template">
        Load Posts
    </button>
    <div id="post-list"></div>
</div>

Den här tillvägagångssätten fungerar med Mustache, Handlebars, eller Nunjucks-templater . Din WebAPI återskapar JSONM SK3 men HTMX hanterarレンereringsclientetMska4 sidaM Ska5 DetMske6 är särskilt användbart när du har en fungerande API eller behöver dela med dig av data med mobila appsMka7 För mer information om HT MX-extensioner inklusive klient, se den medföljande artikeln.

Gotchas & Tips

Debugging-verktyg: Installera HTMX-debuggerextension - den visar varje begäran , respons , och utbyter i realtid

CSRF-tokens

ASP.NET Core's antifalsifierade token behöver särskild handling med AJAXM SK2 HTMX .NET erbjuder flera rena alternativ HtmxAntiforgeryScriptEndpoint:

// In Program.cs
app.MapHtmxAntiforgeryScript();
<!-- In your layout head -->
<script src="@HtmxAntiforgeryScriptEndpoints.Path" defer></script>

Eller använda tagassistenten: <meta name="htmx-config" includeAspNetAntiforgeryToken="true" />. Se Khalid's artikel om HTMX Anti--–Fälgeriska tokens för mer information.

Alpine.js @ Korthand i racer

Alpine's @click korthand konflikter med Razor's @ syntax. Använd det explicita systemet istället:

<button x-on:click="doSomething()">Click me</button>  <!-- Instead of @click -->
<div x-bind:class="isOpen ? 'block' : 'hidden'"></div>  <!-- Instead of :class -->

Eller fly med @@click, men explicit syntax är tydligare

Historiska avdelningar

Kontrollerande historiainträg: Standardmässigt, trycker HTMX varje begäran till historien . För paginationer / filtrerar där du inte vill att historien ska förorenas

<paging model="@Model" hx-push-url="false">  <!-- Don't add history entries -->

Eller använda hx-replace-url="true" att uppdatera URL: s utan att lägga till insatser.

Det Phantompartiella problemet: Browser bakåt/ framåt visar bara en delfragment utan layout. Fixera med hx-history-elt på din innehållsbehållare:

<div class="container mx-auto" id="contentcontainer" hx-history-elt>
    @RenderBody()
</div>

Det talar om för HTMX vilket element att ta fram i ett ögonblick, och bevara omgivningen när man återställer historien

CDN Caching (Cloudflare

Symptom: HTMX fungerar lokalt men bryter sig bakom en CDN - fel innehåll får kasserad

Root Cause: CDN ignorerar HX-Request header. Din server återskapar olika innehåll baserat på den här headern, men CDN kastar dem identiskt.

ASP.NET Core fix Använda VaryByHeaderNames:

[OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" })]

CDN fix: Konfigurera kasregler för att inkludera HX-Request i kask nyckeln. För Cloudflare: Tappbord → Caching | → | Kaskregler |→ | Passformskask nyckel ♫ → | Header ♫ HX-Request.

Debatten om hx-

hx-boost omvandlar normala länkar till AJAX- begäran. Den HTMX-Quirks-sida noterar att några grundteammedlemmar rekommenderar att undvika det (löser <head> innehåll, påverkar lokalitet i beteendet), medan andra tycker att det är bra för snabba vinster

<div hx-boost="true" hx-target="#contentcontainer">
    <a asp-action="Show" asp-route-slug="@post.Slug">Read More</a>
</div>

Denna blogg använder den i stor utsträckning. När det är riktat mot specifika behållare hx-get är tydligare, men hx-boost fungerar om du hx-boost="false" på barnelementen.

Slutsatsen

HTMX med ASP.NET Corepartiklar representerar en återkomst till servern

  • Dynamiska, SPA-liknande interaktioner
  • Server-Seiten rendering (trevlig för SEOM SK2
  • Passande HTTP-キャッシュing
  • Minimal JavaScript
  • Fortskridande förbättring
  • Typ-säkra routering med HTMX.NET
  • Zero-config pagination med mostlylucid.pagingtaghelper

Servern, -, har klarat testet av tiden, och med HTMX,,, har den till slut den eleganta klienten som den förtjänar.-, med hjälp av verktygen som det förtövar. ., du kan bygga robusta webapplikationer med flera årtionden fungerande mönster.

Kompanionsartikel

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

  1. Denna artikel - fokuserar på ASP. Net Core integrering , partiell syn på , HTMX
  2. En vissel-stoppturning av HTMX-extensioner - djupt dyka in i HTMX-eventen , livscykeln , utbyggnadsarkitekturen

Fler HTMX-artiklar

Fortsättning avläsning

officiella dokumentation:

Bibliotek & verktyg:

Samhällens resurser:

Finding related posts...
logo

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