# Успілковий тур по HTMX розширенням та використанню HT MX з ASP.NET Core

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

<!--category-- Javascript, HTMX, ASP.NET Core -->
# Введение

HTMX - це потужна JavaScript-лібература, яка дозволяє створювати динамічні веб-приложения з мінімальним JavaScriptом. Вона дає вам змогу робити AJAX-запити , обмінюватися HTML-контентом МSK2 і обробляти події безпосередньо в ваших HTML-атрибутах. ЯM SK4 я користуюсь HT MX приблизно два роки на даний момент, і з кожним проектом я дізнаюся все більше і більше про його можливості M SK5 і, що важливіше, про обмеження цього '

Однак я досі не вважаю, що маю на це експертні знання. Я просто хотів поділитися деякими речами, які я вивчив на шляху.

> **Article:** Ця стаття зфокусується на системі подій HTMX' МSK1 розширеннях , та заawansowanej налаштуваннях . Для ASPM SK4 NET Конкретні моделі інтеграції з частинними видами [HTMX з ASP.NET Core Partials: ServerM SK2Side Renaissance](/blog/htmx-aspnetcore-partials).

[TOC]

# Мероприяти

## Підготування запиту

Фаза підготовки запиту - це те місце, де HTMX налаштовує запит перед тим, як він відправляється до сервера.. Це включає в себе налаштування заголовків.

```mermaid
flowchart LR

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

```

## Вимагати життєвого циклу

Фаза життєвого циклу запитів - це те місце, де HTMX надсилає запит до сервера і відповідає на нього.

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

## Керування відповіддю

Фаза управління відповіддю - це те, коли HTMX обробляє реакцію з сервера і оновлює DOM. Під час цієї фазы тригуються наступні події

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

```

## Керування історією

Фаза управління історією - це те місце, де HTMX оновлює історію браузера та URL. Під час цієї фазы тригуються наступні подіїM SK1

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

Як ви бачите, HTMX надає безліч подій, до яких можна під 'єднатися для зміни запиту чи відповіді, або навіть історіїM SK1 HT MX робить LOT для того, щоб бути такою компактною системою . Використовуючи кожен з них, ви можете змінити те, як HTX взаємодіє з сервером

# Поширення

Одним із найпотужніших аспектів HTMX є можливість [створити розширення](https://v2-0v2-0.htmx.org/extensions/) розширити свої можливості. У моєму випадку `htmx:configRequest` для додавання додаткових параметрів до запиту. Це корисно, коли ви хочете передати додаткові дані до сервера, не modyfikуючи код HTML або JavaScript

Інші розширення можуть під 'єднатися `htmx:beforeRequest` до того, як запрос буде відправлений;, але **після** більшість інших розширень, які `configRequest`; як у `beforeRequest` такі речі, як `HX-Vals` і `HX-Include`s вже прикріплені до найпершого ( або в payload МSK1 querystringM SK2
Ви можете навіть підключитися `htmx:afterSwap` для виконання дій після заміни контенту. Об 'єднаний з комп' ютерними бібліотеками, такими як [Alpine.js](https://alpinejs.dev/) або [Літ](https://lit.dev/) ви можете створювати потужні динамічні додатки з мінімальним кодом.

HTMX надає деякі вбудовані rozszerzenia, такі як - `hx-boost` і `hx-swap-oob` які дають змогу покращити функцію HTMX без написання будь-якого спеціального коду. Тим не менш

Наприклад:, ви можете додати за замовчанням заголовки до ваших запитів , змінити плату за подачу запиту МSK2 або обробляти певні події у унікальний спосіб

Щоб досягнути цього, HTMX надає вам певні вручні точки інтеграції:

```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 надає низку збудованих przed- розширення, які можна [читайте про це тут](https://htmx.org/extensions/).

Наприклад, гарний [збудовано- в розширенні](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) `json-encode` дозволяє надсилати дані JSON в корпусі запиту замість URL-encoded form dataM SK1 Це корисно, коли ви хочете надіслати складні структури чи массиви даних до сервера
Ви бачите, що це пов 'язано з подіями 3

- `init` - для налаштування розширення і зберігання посилання до API HTMX
- `onEvent` - для набору `Content-Type` заголовок до `application/json` коли запрос конфігурований
- `encodeParameters` - перемикає початковий URL - кодування зашифрованої форми і серіалізує параметри як JSON МSK2 Він також повертає строк, щоб запобігти тому, що HTMX не використовуватиме початкового URL

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

Або навіть простіший, але ще HANDY `hx-debug` розширення, яке додає `HX-Debug` заголовок до запиту. Це корисне для розв 'язання проблем та підключення до журналу , так, що ви можете бачити необроблені дані про запит і відповідь в консольі dev

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

```

Є ще багато таких, як МСК0 і МСК1, включно з дуже потужним [clients side templating extension](https://github.com/bigskysoftware/htmx-extensions/tree/main/src/client-side-templates) що дозволяє вам використовувати клієнтові-side templating библиотеки для перетворення отриманих даних JSON в HTML . Це корисно для створення динамічних інтерфейсів без необхідності покладатися на сервер-side renderingM SK3

# Деякі налаштування

## Цифрові ID динамічних рядів

Наприклад, у нещодавньому проекті я використовував swapи HTMX OOB для оновлення рядків в таблиці.
Для цього я хотів знати, які рядки зараз відображаються в таблиці,, тож я обновив лише ті рядки, які були помітніM SK1

### Поширення

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

### Використовуючи

Щоб його використати, ми повинні додати розширення до нашої HTMX конфігурації.
Отож, у вашому файлі js-помічника (припустимо, що ви ' використовуєте модулі

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

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

Тоді на будь-якому елементі, який ви хочете використати, ви можете додати `hx-ext` атрибут із значенням `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>

```

## Запам 'ятайте параметри

Це ще одне просте розширення HTMX, цього разу підключене до `htmx:configRequest` як ми'ре модифікуємо URL перед тим, як відправити уривок запросуM SK1 Це розширення дуже зручно, якщо ви' використовуєте пошуковий строк на основі фильтрації і т.д. . і хочете, щоб деякі уривки зберігали існуючі фильтры, тоді як інші - не.

Це СIMILAR, але не зовсім те ж саме, що і теперішнє розширення HTMX [push-params](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md)

### Поширення

Ви бачите, що ми `onEvent` щоб слухати `htmx:configRequest` події.

Тоді ми:

- Візьміть елемент, який спричинив подію
- Візьміть `preserve-params-exclude` атрибут з елементу ( якщо він існуєM SK1 і розділити його на сукупність ключів, щоб виключити
- Візьміть поточні параметри URL з локації вікна
- Візьміть нові параметри з URL запиту
- Прокрутить поточні параметри і перевірити, чи вони не є в списку виключення та не є вже в нових параметрах
- Якщо вони не є,, ми додаємо їх до нових параметрів
- Врешті-решт, ми встановили нові параметри для URL запиту і повертаємо true, щоб продовжити роботу з запитомM SK1

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

Тут я використаю [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main) для помічників міток. Як `hx-controller` і `hx-action` є вказівниками, які генерують правильні атрибути HTMX для вас `hx-route-<x>` для wartości, що проходить в маршруті. Це дійсно корисно, адже ви можете використовувати код C#, щоб генерувати правильні значення для атрибутів, замість того, щоб hardcode їх у вашому HTML

### Використовуючи

Будучи розширенням, це дуже просто використовувати

По-перше, треба додати до нашої HTMX конфігурації rozszerzenie.

```javascript

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

NOTA: ВиM SK1 помітите, що стандартні rozszerzenia HTMX використовують метод 'автозавантаження ' для завантаження rozszerzenia

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

Це хороший спосіб зробити це, якщо ви' використовуєте HTMX в не-модульному середовищі -, а(M SK2). Тим не менше,, якщо ви (МSK4) використовуєте модулі (, які ви маєте використовувати(МSK6), то краще використовувати `import` твердження для завантаження rozszerzenia, а потім явно записувати його проти вашого `htmx` instance. Це дозволяє вам скористатися перевагами дерева -shaking і завантажувати лише необхідні розширення

Тоді на вашому елементі ви можете додати `hx-ext` атрибут із значенням `preserve-params` і `preserve-params-exclude` атрибут із відокремленим комасом списком параметрів для виключення з запиту.

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

У цьому випадку через `event.detail.path` мати нове `myparam` значення встановити його в ньому' замінюватиме це одним нашим новим значенням, але зберегти всі інші M SK1за винятком `page` і `sort`). Таким чином, ми можемо далі передавати на сервер будь-які відфільтри, які ми встановили в URL, не переживаючи про їх втрату, коли ми робимо новий запит

# ASP.NET Кора

Однією з найцікавіших речей про HTMX є те, що велика частина його взаємодії з сервером відбувається через HTTP заголовки. Ці заголовка забезпечують сервером багатим контекстом того, що викликало запрошення ,, що дозволяє вам адекватно реагувати від вашого ASP МSK2 NET Коренные кінцеві точки або погляди Razor .

Знову ж таки, ключовою складовою є [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main). Серед багатьох речей, які він надає, є непомітні `Request` розширення для виявлення HTTP-запросів. Це корисно для визначення, чи подан HTMX запрос, чи ні , і для його відповідного виконання

Він також має власний механізм для відправлення триггерів

```csharp

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

Приштовхування Urls і т.д....etcM SK1
Халід зробив чудову роботу, створивши низку поширень, щоб було легше працювати з HTMX в ASP

Це ключеві інструменти в моєму наборі інструментів, коли я працюю з HTMX та ASP. [**Подивіться на це.**](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main)

## Поширені заголовки запитів HTMX

Ось список найпотрібніших заголовків, які HTMX надсилає з кожним запитом.

| Заголовок МSK1 Описание МSK2
|----------------------------|------------------------------------------------------------------------------------------------------------------|
| HXM SK1Рейкція | Завжди вмикається на правильне для будь-якого HTMX-initiated requestMska4 Чудово для виявлення HT MX дзвінків у середній програмі чи контроллерахM Ska5 Мska6
| HXM SK1Target | Идентификаційний номер елементу в DOM, в якому відповідь буде замінена на
| HXM SK1 Триггер | Идентификатор елементу, який триграв запити МSK3eMSC4g. кнопка МSK6 \|
| HXM SK1 Триггер-Назви | Назва триггерного елементу
| HXM SK1Prompt | Составляє вхід користувача з hx-promptMSC4 МSK5
| HXM SK1На даний момент-URL | Веб-браузер URL, коли запрос був започаткований
| HX -HistoryM SK2Restore-Request МSK4 Отримають правильну відповідь, якщо ця просьба є частиною відновлення історії після навігації.

## Запроси про розширення

Я користуюся цим досить широко в своїх програмах ASP.NET Core. Поєднані з такими, як HTMXM SK2Net `Request.IsHtmx()` , `Request.IsHtmxBoosted()` і `Request.IsHtmxNonBoosted()` ви можете легко розпізнати HTTPMX запити і відповісти відповідно.

Наприклад, У мене є дуже простий додаток до Request, який дозволяє мені виявити, чи запити спрямовані на мою головну `#page-content` div. Якщо це так, то я знаю, що повинен відіслати назад частину
NOTA: Багато хто не усвідомлює, що ви можете ввести M SK2повну сторінку ' як частину МSK4 і тоді просто перекидає макет

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

```

## Поширення реагування

Окрім розширення Request,, ви також можете створити Response extensions для відсилання подій назад до клієнта

### Наприклад, SweetAlert

Наприклад, [в моєму інтегруванні SweetAlert2](https://www.mostlylucid.net/blog/usingsweetalertforhxindicators) Я уможливлю закрити диалог, використовуючи триггер, який встановлений з сервера.

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

```

Це активується з серверу як HTMX триггер-реакція.

```csharp

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

    }
```

Це триггерує `sweetalert:close` події з боку клієнта,, що дозволяє закрити диалогM SK1 Дані можна передати назад клієнту за допомогою `HX-Trigger` заголовок. Це корисно для передачі даних від сервера до клієнта, не modyfikуючи код HTML чи JavaScriptM SK1

Як ви бачите, МСК0 легко слухати ці події, просто додавши до тіла слухача подій. МSK1 Я користуюся JSON, бо він завжди правильно кодує.

### Подивіться

Раніше я писав про свій тост [тут](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx), але це також варто згадати ' так само як і тут МSK2 Для того, щоб дуже просто дозволити серверу триггерувати paziņoння про тост з боку клієнта \ . \ Я встановив тригнер у цьому розширенні Відповіді

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

    }
```

Потім я підключюсь до клієнта події і зателефоную йому `showToast` функція.

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

window.showHTMXToast = showHTMXToast;

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

Це змушує мене `showToast` функція і добре, показує тостM SK1 знову погляньте на це більше [у статті ](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);
}
```

## На завершення

Що ж, це HTMX та ASP-сервіс. Я сподіваюся, що ви зрозуміли, що це дуже корисно і цікаве.

## Подібні статті в цьому блогі

### Article Companion

Ця стаття є частиною серії двох частин на HTMX з ASP

1. **[HTMX з ядром ASP.NET](/blog/htmx-aspnetcore-partials)** - зосереджена на ASPM SK1 NET Коренная інтеграція , частинні переглядиМSK3 HTMX.NETMSC5 та відображання
2. **Цей artykuł** - Глубокий занурення в події HTMX , життєвий цикл МSK2 архітектура rozszerzenia

### Більше статей про HTMX

- [Доdanie плагу з HTMX](/blog/addpagingwithhtmx) - Implementing pagetion with HTMX
- [ASP.NET Корінь Caching з HTMX](/blog/aspnetcachingwithhtmx) - Стратегії шифрування для HTTPMX запитів
- [Авто- переробка з альпіальним та HTMX](/blog/autorefreshwithalpineandhtmx) - Об 'єднання Alpine.js з HTMX для реактивних компонентів
- [Зробити ваш сайт більш SPA-подібним до HTMX](/blog/htmxtomakeyoursitemorespalike) - SPAM SK1подібна навігація без фреймворків JavaScript
- [Використовуючи SweetAlert для індикаторів HX](/blog/usingsweetalertforhxindicators) - Красиві вказівники навантаження з HTMX
- [Показ Toast і Swapping з HTMX](/blog/showingtoastandswappingwithhtmx) - Сервер

## Дальне читання

**офіційна документація HTMX:**

- [Документація HTMX](https://htmx.org/docs/) - офіційна документація HTMX
- [Справка про події HTMX](https://htmx.org/events/) - Полний список подій HTMX
- [Поширення HTMX](https://htmx.org/extensions/) - офіційні розширення HTMX
- [Наприклад, HTMX](https://htmx.org/examples/) - Практичні приклади шаблонів HTMX
- [есе HTMX](https://htmx.org/essays/) - Роздумливі статті про філософію HTMX

**Бібліотеки & НаrzędziаM SK1**

- [HTMX.NET GitHub](https://github.com/khalidabuhakmeh/Htmx.Net) - вихідний код бібліотеки HTMX.NET
- [HTMX.NET NuGet](https://www.nuget.org/packages/Htmx/) - HTMX.NET на NuGet
- [Сайт Халида Абуахмеха](https://khalidabuhakmeh.com/) - Творець HTMX.NET
- [Alpine.js Dokumentacja](https://alpinejs.dev/) - Официальні альпійські доки

**Community Resources:**

- [HTMX Розбіжність](https://htmx.org/discord) - Активна підтримка громади
- [Кітап систем Hypermedia](https://hypermedia.systems/) - Бесплатна книга онлайн про створення гіпермедійних програм