A Wistle- stop Tour of HTMX Extensions and Використовується HTMX з ядром ASP. NET (Українська (Ukrainian))

A Wistle- stop Tour of HTMX Extensions and Використовується HTMX з ядром ASP. NET

Friday, 02 May 2025

//

16 minute read

Вступ

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

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

Стаття подружнього партнера: У цій статті йдеться про систему подій HTMX, суфікси та розширені налаштування. Для візерунків ядра ASP. NET з частковими переглядами, HTMX. NET і пагінією дивіться мою супровідну статтю: HTMX з частковостями ядра ASP.NET: Ренесансу серверів.

Події

Запит щодо приготування

Фаза приготування запиту полягає у тому, де HTMX налаштовує запит перед надсиланням його на сервер. Серед цих параметрів встановлення заголовків, додавання параметрів і обробки вхідних даних користувача. Під час цієї фази буде увімкнено такі події:

flowchart LR

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

Запит на життєвий цикл

Пропозиційна фаза життєвого циклу полягає у тому, що HTMX надсилає запит на сервер і керує відповіддю. Наступні події буде увімкнено протягом цієї фази:

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

Обробка відповідей

У фазі роботи з відповідями HTMX обробляє відповідь на сервер і оновлює 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]

Керування журналом

Кроком керування журналом є те, де HTMX оновлює журнал переглядача і адресу URL. Наступні події буде викликано протягом цієї фази:

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

Як ви бачите, у HTMX передбачено декілька подій, до яких ви можете прив' язатися, щоб змінити запит або відповідь, або навіть історію; HTMX виконує роботу LOT для такої компактної системи. За допомогою кожної з цих систем ви можете змінити спосіб, у який HTMX взаємодіятиме з сервером / клієнтом у досить широкий спосіб.

Додатки

Одним з найпотужніших аспектів HTMX є здатність до створити суфікси назв щоб розширити його можливості. у моєму випадку, як правило, htmx:configRequest щоб додати до запиту додаткові параметри. Це корисно, якщо ви бажаєте передати додаткові дані серверу без зміни коду HTML або JavaScript.

Інші суфікси можуть мати гачок htmx:beforeRequest змінити запит перед надсиланням, але після більшість інших суфіксів, які з' єднують configRequest; як в beforeRequest елементи на зразок HX-Vals і HX-Includes вже прив' язано до лунки (або у рядку запиту\ query). Ти можеш навіть гайнути. htmx:afterSwap призначено для виконання дій після зміни вмісту. Комбіновано з клієнтськими бібліотеками, на зразок Альпійський.js або Літ Ви можете створювати потужні динамічні програми з мінімальним кодом.

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

Наприклад, у вас може виникнути потреба у додаванні нетипових заголовків до ваших запитів, зміні навантаження на запити або обробці окремих подій у унікальний спосіб.

Щоб досягти цього ГТМX, ви можете скористатися такими зручними точками інтеграції:


{
  /**
   * 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 - це декілька попередньо вбудованих додатків, які ви можете надати прочитати тут.

Наприклад, зручно вбудований суфікс json-encode Надає вам змогу надсилати дані JSON у тілі запиту замість даних форми, закодованих за адресою URL. Ця можливість буде корисною, якщо ви бажаєте надіслати на сервер складні структури або масиви даних. Ви можете побачити, що цей гачок вміщується в 3 події

  • init - для налаштування розширення і збереження посилання на API HTMX
  • onEvent - встановити Content-Type заголовок до application/json якщо запит налаштовано
  • encodeParameters - для перевизначення типового кодування форми, закодованого за адресою URL і послідовного кодування параметрів JSON. Крім того, буде повернуто рядок, за допомогою якого HTMX не використовуватиме типове кодування формату, закодоване за адресою URL.
(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))
    }
  })
})()

Або навіть простіше, але НАВІТЬ БІЛЬШЕ РУКИ hx-debug суфікс, який додає HX-Debug заголовок запиту. Цей пункт буде корисним для усування вад і використання журналу, оскільки він надасть вам змогу переглянути дані щодо запиту та відповіді у консолі dev.

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

Є багато більше, включаючи дуже потужну розширення інтерфейсу програми на стороні клієнта Це надає вам змогу використовувати клієнтські бібліотеки для перетворення отриманих даних JSON у HTML. Це корисно для створення динамічних UI без довіри до відображення на стороні сервера.

Деякі нетипові розширення

Ідентифікатори динамічного рядка

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

Розширення

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 (якщо ви використовуєте модулі; йоу має бути) ви можете зробити щось на зразок цього:

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

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

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

Зберігати параметри

Це ще одне просте розширення HTMX, на цей раз з'єднане з htmx:configRequest оскільки ми змінюємо адресу URL перед надсиланням запиту. Цей суфікс буде корисним, якщо ви використовуєте фільтрування, засноване на запитах тощо, і бажаєте, щоб деякі запити зберігали існуючі фільтри, а інші - не (наприклад, " назва " і " startdate ," але не " page " або " sort ").

Це SIMILAR, але не зовсім те саме, що існуюче розширення HTMX stop- params

Розширення

Ви бачите, що ми гачок onEvent слухати htmx:configRequest Замечательно.

Тоді ми:

  • Отримати елемент, який викликав подію
  • Get the preserve-params-exclude атрибут елемента (якщо такий існує) і поділено на масив ключів для виключення (отже, ми не додаємо їх до запиту)
  • Отримати поточні параметри URL з адреси вікна
  • Отримати нові параметри з URL запиту
  • Прокручувати поточні параметри і перевіряти чи їх немає у списку виключення, а не у нових параметрах
  • Якщо їх немає, ми додаємо їх до нових параметрів.
  • Нарешті, ми встановимо нові параметри до URL запиту і повернемо " true," щоб продовжити запит.
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 Допоміжні помічники, такі, як hx-controller і hx-action є помічниками міток, які створюють правильні атрибути HTMX для вас. Крім того, Do not translate the keyword between brackets (e. g. ServerName, ServerAdmin, etc.) hx-route-<x> значення, які слід передати маршруту. Цей параметр дуже корисний, оскільки надає вам змогу використовувати C# код для створення правильних значень для атрибутів, а не для жорсткого кодування їх у HTML.

Використання

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

Спочатку нам потрібно додати суфікс до нашої конфігурації HTMX.


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

ЗАУВАЖЕННЯ: ви помітите, що типові розширення HTMX використовують метод " автозавантаження " для завантаження додатка.

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

Це непоганий спосіб зробити це, якщо ви використовуєте HTMX у немодульному середовищі. Але, якщо ви використовуєте модулі (якими ви маєте бути), краще використовувати ці модулі import оператор, який слід завантажити суфікс, а потім явно зареєструвати його на вашу htmx Приклад. За допомогою цього пункту ви зможете скористатися перевагами пересування деревом і завантажити лише потрібні вам розширення.

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


<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 значення, встановлене у цій змінній, замінить її на наше нове значення, але збереже всі інші (окрім page і sortОтже, ми можемо передавати будь - які фільтри, встановлені у адресі URL на сервер, не турбуючись про те, що вони програються, коли ми робимо новий запит.

Ядро ASP. NET

Однією з акуратних речей, пов' язаних з HTMX, є те, що більшість її взаємодії з сервером відбувається за допомогою заголовків HTTP. За допомогою цих заголовків ви можете вказати сервер з багатим контекстом того, що було викликано запитом, що надасть вам змогу відповідати відповідно з ваших кінцевих точок ядра ASP. NET (ASP. NET) або Razor.

Знову ключовим компонентом цього є HTMX.Net. Між багатьма речами, яких вона постачає є охайні Request суфікси для виявлення запитів HTMX. Цей параметр корисний для визначення, чи було виконано запит HTMX, чи ні, і відповідно до цього можна його виконати.

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


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

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

Це ключовий інструмент у моїй панелі інструментів під час роботи з HTMX і ASP.NET Core. Заціни!

Загальні заголовки запитів HTMX

Тут йде спад найкорисніших заголовків, які HTMX надсилає з кожним проханням:

. |----------------------------|------------------------------------------------------------------------------------------------------------------|

HX- Request} Завжди встановлюється у значення true для будь- якого запиту на запит HTMX- ініціалізації. Великою перевагою для виявлення дзвінків HTMX у середній системі або контролерах.

♪ HX-Target ♪ [д] елементу цілі у DOM, на який буде обмінюватися реакція.' ♪ HX-Tr} id елемента, який викликає запит (e. g. a button). * ♪ HX-Trger-} [Назва крапального елементу (використовуйте для форм).} ♪ HX-Prompt використовує вхідні дані від hx-prompt.} ♪ HX- Current- URL} URL навігатора, коли було висунуто запит. Корисно для ведення лісозаготівель та контексту. ♪ HX- History- Restorre-Request ♪} якщо запит є частиною історії відновлення після навігації (e.g, back button).

Запит розширеньThe role of the transaction, in present tense

Я досить широко використовую ці додатки у своїй програмі ASP.NET. Комбіновано з програмами HTMX.Net, на зразок Request.IsHtmx() , Request.IsHtmxBoosted() і Request.IsHtmxNonBoosted() ви легко можете визначити запити HTMX і відповідно відповісти на них.

Наприклад, у мене є дуже простий суфікс для запиту, за допомогою якого я можу визначити, чи не націлився мій головний запит. #page-content Якщо це так, то я знаю, що мені слід відправити назад частинку. ЗАУВАЖЕННЯ: багато хто не розуміє, що можна вказати частинну " повну сторінку ," а потім просто пропустити компонування.

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

Розширення відповіді

Окрім суфіксів запиту, ви також можете створити розширення відповіді для надсилання записів подій до клієнта. Це корисно для вмикання подій на стороні клієнта.

Приклад SweetAlert

Наприклад, в моїй інтеграції SweetAlert2 Увімкнути закриття діалогового вікна за допомогою перемикача, який встановлено на сервері.

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

Ця дія активується з сервера як подія- перемикач HTMX.


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

    }

За допомогою цього пункту можна увімкнути sweetalert:close запис події на стороні клієнта, який надасть вам змогу закрити діалогове вікно. Крім того, ви можете передати дані клієнту за допомогою команди HX-Trigger заголовок. Цей пункт буде корисним для передавання даних з сервера на клієнт без потреби у зміні коду HTML або JavaScript.

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

ShowToast

Раніше я писав про свій метод тостів. тутПро це варто також згадати. Щоб просто дозволити серверу запустити сповіщення про тост на стороні клієнта, я встановила гачок у цьому розширенні відповіді.

    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 функція.

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

window.showHTMXToast = showHTMXToast;

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

Це кличе мене. showToast function і well, показує тост; знову ж таки, щоб дізнатися більше про це у статті .



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

Включення

Ну ось і все, екскурсія по GTMX і ASP.NET Core. Я сподіваюся, що це буде корисним і інформативним. Якщо у вас є питання або коментарі, будь ласка, не вагайтеся коментувати нижче.

Супутні статті щодо цього блогу

Стаття для друзів

Ця стаття є частиною двосторонньої серії HTMX з ядром ASP. NET:

  1. HTMX з частинками ядра ASP. NET - Зосереджується на інтеграції ядра ASP.NET, часткових переглядах, HTMX.NET і пагіні
  2. Ця стаття - Глибоко занурившись у події HTMX, життєвий цикл, архітектуру розширення та нетипові розширення

Додаткові статті HTMX

Подальше читання

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

& Інструменти бібліотек:

Ресурси спільноти:

  • HTMX Dispord - Підтримка активного спільноти
  • Книга hypermedia Systems - Безкоштовна онлайн книга про створення гіпермедіадичних програм
Finding related posts...
logo

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