# Whistle-stop Tour of HTMX Extensions and Using HT MX with ASP.NET Core

<datetime class="hidden">2025-05-02T20:30</datetime>

<!--category-- Javascript, HTMX, ASP.NET Core -->
# Esitys

HTMX on tehokas JavaScript-libraari, joka antaa mahdollisuuden luoda dynaamisia web- sovelluksia vähimmäisJavaScriptin avulla.

En kuitenkaan vieläkään väitä, että minulla olisi asiasta asiantuntemusta. Halusin vain kertoa teille joitakin niistä asioista, jotka olen oppinut tällä tiellä.

> **Yhteinen artikla:** Tässä artiklassa keskitytään HTMX-':n tapahtumajärjestelmään, ,:n laajennusten ja pitkälle kehitettyjen räätälöintien käyttöön..:n käyttö ASP:ssa sekä .:ssä ja NETissä. [HTMX ASP:n kanssa](/blog/htmx-aspnetcore-partials).

[TOC]

# Tapahtumat

## Pyynnön valmistelu

Pyynnön valmisteluvaihe on se, jossa HTMX varmistaa pyynnön ennen kuin se lähetetään serverille.. Tähän sisältyy headerien asettaminen.

```mermaid
flowchart LR

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

```

## Pyyntö elinkaari

Pyynnön elinkaaren vaiheessa HTMX lähettää pyynnön serverille ja käsittelee vastausta. Tämän vaiheen aikana käy ilmi seuraavat tapahtumat

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

## Vastauksen käsittely

Vastauksen käsittelyvaihe on, kun HTMX käsittelee vastausta palvelimelta ja päivittää DOMin.

```mermaid
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]

```

## Historian hallinta

Historianhallintavaihe on HTMX:n ajantasaistaminen hakukoneen historiasta ja URL:stä. Tämän vaiheen aikana käynnistetään seuraavat tapahtumat

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

Kuten näette, HTMX tarjoaa joukon tapahtumia, joihin voi liittyä pyynnön tai vastauksen muuttamiseksi.

# Laajennus

Yksi tehokkaimmista HTMX:n näkökohdista on kyky [laajentaminen](https://v2-0v2-0.htmx.org/extensions/) laajentaa toimintavalmiuksiaan. Omassa tapauksessani `htmx:configRequest` lisätä lisäparametereja pyyntöön. Tämä on hyödyllistä, kun haluatte siirtää lisätietoja palveluun muuttamatta HTML:n tai JavaScriptin koodia

Muut laajennukset saattavat liittyä yhteen `htmx:beforeRequest` muuttamaan pyyntöä ennen kuin se lähetetään;, mutta **sen jälkeen** useimmat muut lisäykset, jotka kiinnittävät `configRequest`; sellaisena kuin `beforeRequest` asiat, kuten `HX-Vals` ja `HX-Include`s on jo liitetty toissijaiseen ( joko maksutaakkaan \ kysymyksenpyynnön stringiin
Voitte jopa kiinnittää `htmx:afterSwap` toimien toteuttamiseen sen jälkeen, kun sisältö on vaihdettu. Yhdennetty client side templating librariesin kuten [Alpine.js](https://alpinejs.dev/) tai [Liitti](https://lit.dev/) voitte luoda tehokkaita dynaamisia sovelluksia vähimmäiskoodin kanssa.

HTMX tarjoaa joitakin rakenteellisia -in lisäyksiä, kuten `hx-boost` ja `hx-swap-oob` jotka mahdollistavat HTMX:n toiminnallisuuden parantamisen kirjoittamatta mitään räätälöityä koodia. On kuitenkin tapauksia, joissa on tarpeen luoda omat lisäykset erityistarpeiden täyttämiseksi

Esimerkiksi, saatatte haluta lisätä pyyntöihinne räätälöityjä otsikoita, muuttaa pyynnön maksutaakkaaM SK2 tai käsitellä erityisiä tapahtumia ainutlaatuisella tavalla

Tämän saavuttamiseksi HTMX tarjoaa joustavat integrointipisteet:

```javascript

{
  /**
   * 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 tarjoaa useita entiseen - -järjestelmään tehtyjä lisäyksiä, joita voi [lukekaa siitä täällä](https://htmx.org/extensions/).

Esimerkiksi hyödyllinen [built-in laajentaminen](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) `json-encode` sallii JSON-tietojen lähettämisen pyynnön kohdassa URL:n sijasta-encodoitujen lomakkeiden tietojen sijaan . Tämä on hyödyllistä, kun haluatte lähettää monimutkaisia tietorakenteita tai taulukoita palveluun
Voitte nähdä, että tämä liittyy 3 tapahtumiin

- `init` - laajentaminen ja viittauksen säilyttäminen HTMX-API:hen
- `onEvent` - asettaa `Content-Type` header to `application/json` kun pyyntöä konfiguroidaan
- `encodeParameters` - ei-todellisen URL:n ylipäätökseen ottamisesta -kodoidun lomakkeen kodifioimisesta ja parametrien serialisoimisesta JSONinaM SK2 Se myös palauttaa stringin, jolla estetään HTMX käyttämästä default-URL:tä-kodoitua lomakettaMSC4

```javascript
(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))
    }
  })
})()
```

Tai jopa yksinkertaisempi, mutta vieläkin käsittävämpi `hx-debug` lisäys, joka lisää `HX-Debug` pyynnön otsikko. Tämä on hyödyllinen parannuskeinojen ja logging-tarkoituksiin,, koska se antaa mahdollisuuden nähdä raakapyynnön ja vastauksen tiedot dev-konsolissa

```javascript
(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')
      }
    }
  })
})()

```

':ta on paljon enemmän, myös erittäin tehokas [Client side templating extension](https://github.com/bigskysoftware/htmx-extensions/tree/main/src/client-side-templates) , jonka avulla voidaan käyttää client-välistä templointia käyttäviä kirjastoja muuntamaan palautetut JSON-tiedot HTML:iin. Tämä on hyödyllistä dynaamisten UI:ien luomiseksi ilman, että tarvitsee turvautua serveriin -välin renderoimiseen

# Jotkin räätälöityt lisäykset

## Dynaamiset  Row ID:t

Esimerkiksi äskettäisessä hankkeessa käytin HTMX OOB-vaihtoehtoja, jotta ajantasaistetaan useita riviä taulukossa.
Tätä varten halusin tietää, mitkä rivit esitetään tällä hetkellä taulukossa.

### Laajentaminen

```javascript
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
    }
}
```

### Käyttämällä sitä

Jotta sitä voitaisiin käyttää, meidän on lisättävä HTMX-configurationimme lisäys.
Siten omassa lähtökohdassanne js-tiedostossa ( olettaen, että olette käyttämässä mallejaM SK2 teidän pitäisi olla ) voitte tehdä jotain tämänkaltaista

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

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

Sitten mihin tahansa elementtiin, jota haluatte käyttää, voitte lisätä `hx-ext` ominaisuuksia, joiden arvo on `dynamic-rowids`.

```html
                <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>

```

## Varmistakaa kohdat

Tämä on toinen yksinkertainen HTMX-lisäys,, joka on tällä kertaa sidottu `htmx:configRequest` , kun muutamme URL:tä ennen pyynnön lähettämistä. Tämä laajentaminen on hyödyllistä, jos haluatte, että jotkin pyynnöt säilyttävät olemassa olevat filtrit ja toiset eivät

Tämä on samanlainen mutta ei täysin sama kuin nykyinen HTMX-laajennus [push-params](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md)

### Laajentaminen

Kuten voitte nähdä, `onEvent` kuunnella `htmx:configRequest` tapahtuma.

Sitten me:

- Otetaan elementti, joka käynnisti tapahtuman
- Ottakaa `preserve-params-exclude` , jos se on olemassa) ja jakaa sen avainkokonaisuudeksi poistaakseen (, joten emme lisää niitä pyyntöön
- Ottakaa nykyiset URL-parameterit ikkunan sijaintipaikasta
- Ottakaa uudet parametrit pyynnön URL:stä
- Läpäisy nykyisten parametrien läpi ja tarkistaa, eivätkö ne ole poissuljettu luettelossa eivätkä jo uudet parametrit
- Jos ne eivät ole,, lisäämme ne uusiin parametreihin
- Lopuksi, asetamme uudet parametrit pyynnön URL:hen ja palautamme totuuden jatkaaksemme pyyntöä.

```javascript
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;
    }
};
```

Käytän tässä olennaista [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main) its tag helpers. `hx-controller` ja `hx-action` ovat merkintähelporeita, jotka tuottavat teille oikeat HTMX-ominaisuudet. sekä `hx-route-<x>` on todella hyödyllistä, koska se antaa mahdollisuuden käyttää C-koodia todellisten arvojen luomiseksi ominaisuuksille sen sijaan, että niitä olisi kodifioitava HTML:ssänne.

### Käyttämällä sitä

Koska se on laajennus, sitä on todella helppo käyttää

Ensinnäkin meidän on lisättävä HTMX-configurationimme laajennus.

```javascript

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

PANEE MERKILLE: Teidän on huomautettava, että HTMX:n tavanomaiset laajennusmenetelmät käyttävät 'autoloadM SK3 menetelmää ladatakseen laadinnon.

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

Tämä on hyvä tapa tehdä se, jos olette käyttämässä HTMX:tä muussa kuin - -muunnelmaympäristössä. `import` julistus ladata laajennus ja sitten nimenomaisesti rekisteröidä se vastauksena `htmx` instance. Tämä mahdollistaa sen, että hyödynnätte puun rakennetta -shakingia ja laaditte vain tarvitsemanne laajennusten

Sitten elementtin voin lisätä `hx-ext` ominaisuuksia, joiden arvo on `preserve-params` ja `preserve-params-exclude` periaate, johon sisältyy selitetty luettelo parametreista, joita pyynnöstä on poistettava.

```html

<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>
```

Tässä tapauksessa `event.detail.path` uusi `myparam` arvo asettaa sen siihen' korvaa tämän uuden arvomme, mutta säilyttää kaikki muut M SK1 lukuun ottamatta `page` ja `sort`). Voimme siis siirtää kaikki URL-osoitteessa määritellyt filtrit edelleen palveluun ilman, että meidän on huolehdittava niiden katoamisesta, kun teemme uuden pyynnön.

# ASP.NET Core

HTMX:n eräs hieno asia on se, että suuri osa sen vuorovaikutuksesta serveriin tapahtuu HTTP-otsikkojen välityksellä. Nämä otsikot antavat serverille rikkaan kontekstin siitä, mikä teki pyynnön lähtökohdaksi.

Jälleen kerran tämän keskeinen osatekijä on [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main). Monista sen tarjoamista tuotteista monet ovat huolellisia `Request` HTMX-pyyntöjen havaitsemiseen tarkoitettuja lisäyksiä. Tämä on hyödyllinen, jotta voidaan määritellä, tekikö HT MX pyynnön vai ei, ja käsitellä sitä vastaavasti

Sillä on myös oma mekanismi,

```csharp

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

Push Urls jne...etc...
Khalid teki loistavan työn laatiessaan joukon lisäyksiä, jotka helpottavat HTMXin käyttöä ASP:ssa

HTMX:n ja ASP:n kanssa työskennellessäni se on keskeinen väline. [**Katsokaa se!**](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main)

## Yleiset HTMX-pyynnön otsikot

Tässä on HTMX:n lähettämien hyödyllisimpien otsikoiden jakautuminen jokaiseen pyyntöön.

| Luettelo  |
|----------------------------|------------------------------------------------------------------------------------------------------------------|
| HX
| HXM SK1Target | DOM:n kohdeelementin ID, johon vastaus korvataan
| HX
| HX
| HX
| HX
| HX -Historia-RestoreM SK3Täkymys SSK4 Lähetetään totuudenmukaisesti, jos pyyntö on osa historian palauttamista navigoinnin jälkeen.

## Pyydän lisäyksiä

Käytän näitä melko laajasti ASP:ssani `Request.IsHtmx()` , `Request.IsHtmxBoosted()` ja `Request.IsHtmxNonBoosted()` voitte helposti havaita HTMX-pyynnöt ja vastata niihin..

Esimerkiksi, Minulla on todella yksinkertainen lisäys pyyntöön, joka antaa minulle mahdollisuuden havaita, kohdennetaanko pyyntö maininiin. `#page-content` div. Jos se on, niin tiedän, että minun pitäisi lähettää takaisin osittainen
PANEE MERKILLE: Monet eivät realize you can specify a 'full pageM SK3 as a partial , it then just skips the layout

```csharp
        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;
    }
    }

```

## Vastauksen laajentaminen

Pyyntöjen laajennusten lisäksi, voitte myös luoda vastauksen laajennettuja toimenpiteitä tapahtumien lähettämiseksi takaisin klientilleM SK1 Tämä on hyödyllistä Client-puolisten tapahtumista aktivoimiseksi.

### SweetAlert -esimerkki

Esimerkiksi [SweetAlert2 integroinnissani](https://www.mostlylucid.net/blog/usingsweetalertforhxindicators) Saanen lopettaa vuoropuhelun käyttämällä lähtölaukauksen, joka on asetettu palvelijalta.

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

```

Tämä aktivoidaan palvelimelta HTMX:n aktivointitapahtumana.

```csharp

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

    }
```

Tämä käynnistää `sweetalert:close` tilanteesta riippuvainen tapahtuma,, joka mahdollistaa vuoropuhelun sulkemisen `HX-Trigger` header. Tämä on hyödyllinen tietojen välittämiseksi palvelimelta Clientiin muuttamatta HTML- tai JavaScript-koodia

Kuten näette' on täysin helppo kuunnella näitä tapahtumia vain lisäämällä tapahtumien kuuntelijan kehoonM SK1 Käytän JSONia pääasiassa siksi, että se sekoittaa aina asianmukaisesti

### ShowToast

Olen kirjoittanut aiemmin savutusmenetelmästäni [täällä](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx), mutta se ' kannattaa mainita myös täälläM SK2 Voidakseni hyvin yksinkertaisesti aktivoida serverin toast-ilmoituksen Client-puolelle. Asetsin aktivaattorin tähän vastauksen laajennuseen

```csharp
    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
            }
        }));

    }
```

Siirryn sitten tapahtuman asiakkaiden puoleen ja kutsun `showToast` Funktio.

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

window.showHTMXToast = showHTMXToast;

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

Tämä kutsuu sitten minuun `showToast` function and well, osoittaa toastM SK1 jälleen nähdä enemmän siitä [artiklassa ](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx).

```javascript


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);
}
```

## Päätelmissä

No niin, että's se,hirlwind Tour of HTMX and ASP .NET CoreM SK3 Toivon, että olette löytäneet sen hyödylliseksi ja informativeksiMSC4 Jos teillä on kysymyksiä tai huomautuksia, voitte ystävällisesti kommentoida sitä jäljempänä.

## Related Articles on This Blog

### Yhteinen artikla

Tämä artikla on osa HTMX:n kahta osasarjaa ASP:n kanssa

1. **[HTMX ASP.NETin ydinosapuolten kanssa](/blog/htmx-aspnetcore-partials)** - keskittyy ASP:iinM SK1NETin ydinintegraatioon , osittaisiin näkemyksiin M SK3 HTMX.NETMSC5 ja sivuuttamiseen
2. **Tämä artikkeli** - HTMX-tapahtumiin syvällinen virtaaminen , elinkaari

### Lisätietoja HTMX-artikkeleista

- [HTMX:n kanssa vuoropuhelujen lisääminen](/blog/addpagingwithhtmx) - HTMX:n kanssa toteutettava sivuutus
- [ASP.NET Core Caching HTMXin avulla](/blog/aspnetcachingwithhtmx) - HTMX-pyyntöjen tallentamisstrategiat
- [Auto-refresh with Alpine and HTMX](/blog/autorefreshwithalpineandhtmx) - Alpine'n ja HTMX:n yhdistäminen reaktiivisiin komponentteihin
- [Siten lisääminen SPA-kuten HTMX-järjestelmässä](/blog/htmxtomakeyoursitemorespalike) - SPA-navigointi ilman JavaScript-puitteita
- [SweetAlertin käyttö HX-indikaattorien osalta](/blog/usingsweetalertforhxindicators) - kauniit lastausindikaattorit HTMX:llä
- [Toast- ja Swapping HTMX:n avulla](/blog/showingtoastandswappingwithhtmx) -Serveri-pyynnellyt toast-ilmoitus

## Lisä lukeminen

**Virallinen HTMX-asiakirja:**

- [HTMX-asiakirja](https://htmx.org/docs/) - Virallinen HTMX-asiakirja
- [HTMX-tapahtumat](https://htmx.org/events/) - HTMX-tapahtumien täydellinen luettelo
- [HTMX-laajennukset](https://htmx.org/extensions/) - viralliset HTMX-laajennukset
- [HTMX-esimerkit](https://htmx.org/examples/) - HTMX-muodon käytännön esimerkkejä
- [HTMX-kertomukset](https://htmx.org/essays/) - HTMX-filosofiaa koskevat ajatuksia herättävät artikkelit

**Luettelo kirjastoista & välineet:**

- [HTMX.NET GitHub](https://github.com/khalidabuhakmeh/Htmx.Net) - HTMX-lähdekoodi
- [HTMX.NET NuGet](https://www.nuget.org/packages/Htmx/) - HTMX.NET NuGetissä
- [Khalid Abuhakmeh MSK:n verkkosivusto](https://khalidabuhakmeh.com/) - HTMX:n luoja
- [Alpine.js-asiakirja](https://alpinejs.dev/) - Virallinen alppimaajoukkue

**Yhteisön resurssit:**

- [HTMX-erimistö](https://htmx.org/discord) - Aktiivinen yhteisötuki
- [Hypermedia Systems -kirja](https://hypermedia.systems/) - Vapaa verkkokirja hypermedia-sovellusten rakentamisesta