# HTMX扩展和以 ASP=.NET 核心使用HTMX

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

<!--category-- Javascript, HTMX, ASP.NET Core -->
# 一. 导言导言 导言 导言 导言

HTMX 是一个强大的 JavaScript 库, 它使您能够以最小的 JawaScript @ . 创建动态网络应用程序。 它能让 AAX 请求 MS K1 交换 HTML 内容\ MPK2 并直接处理您的 HTMl 属性中的事件@. I' 在目前使用HTMX大约两年的时间里, 每个项目我都越来越了解它的能力\ I;,更重要的是, It_'s限制=.

不过我还是不知道 'T 声称对它有专业知识 我只是想分享一些 在路上学到的东西

> **附录 条款** 本篇文章的焦点是 HTMX'}活动系统=,}扩展_,}和高级自定义@.}*ASP{.}*NET核心融合模式(部分浏览)<,}HTMX.}NET}MS K7}♪和射程〞,}请见我的配套文章 □:} [HTMX 与 ASP.NET 核心部分@:服务器 @-Side复兴](/blog/htmx-aspnetcore-partials).

[TOC]

# 事件活动

## 请求准备

请求准备阶段是 HTMX 在发送到服务器@ .} 这包括设置信头 @ MS K1} 添加参数 ,} 并处理用户输入.\ 。 在此阶段启动以下事件=:}

```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 .}(以下事件在此阶段触发) @:

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

您可以看到 HTMX 提供了一些事件, 您可以用来修改请求或响应@ , @ or even record *;}HTMX 因为是一个如此紧凑的系统而做一个LOT =\ MS K2 使用每一个这些您可以修改 HTM X 如何以非常全面的方式与服务器 {/} 客户端进行互动\ MPK4}

# 延长

HTMX最强大的一个方面是: [创建扩展名](https://v2-0v2-0.htmx.org/extensions/) 来扩展它的能力... .... 在我看来... `htmx:configRequest` 以在请求中添加附加参数@.}当您想要将额外数据传送到服务器而不必修改 HTML 或 JavaScript 代码 . 时,此功能是有用的 @ action

其他分机可能会被勾钩 `htmx:beforeRequest` 在发送前修改请求@;}但 **之后** 勾勾勾 `configRequest`; 如 `beforeRequest` 类似的东西 `HX-Vals` 和 `HX-Include`在有效载荷中 [{\查询}).}已连接到 reuest {(} 。
你连勾钩都行 `htmx:afterSwap` 在内容被互换后执行动作@. @% 和客户端游戏库合并 [阿尔卑斯山](https://alpinejs.dev/) 或 [文学](https://lit.dev/) 您可以以最小的代码创建强大的动态应用程序@ . @

HTMX 提供一些内建的“-”扩展名, 如 `hx-boost` 和 `hx-swap-oob` 允许您增强 HTMX 的功能, 不写入任何自定义代码@.}然而 @ , 有时需要创建您的扩展名以满足特定要求\.

例如,您可能想要在您的请求中添加自定义信头@ ,}修改请求有效载荷 @ MS K2 或者以一种独特的方式处理特定事件\ .}

实现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 提供一些预设的“% -” 扩展件 [有关在这里读取](https://htmx.org/extensions/).

例如便用 [内建的-in 扩展](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) `json-encode` 允许您在请求的正文中发送 JSON 数据, 而不是 URL -\encodd form drops data.。 如果您想要向服务器发送复杂的数据结构或数组, 这将有用@.}
您可以看到这个连接到 @ 3}事件中的钩子Name

- `init` - 以设置扩展名并存储 HTMX API 的参考文件
- `onEvent` - 设置 `Content-Type` 标题到 `application/json` 当请求被配置时
- `encodeParameters` - 以推翻默认的 URL MS K1encoded 窗体编码, 并将参数序列化为 JSON}. 并返回一个字符串以防止 HTMX 使用默认 URl -enccod 表格编码@.

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

甚至更简单但甚至更简单的人类 `hx-debug` 附加外加 `HX-Debug` 请求标题@.}% 此选项可用于调试和记录目的 @ , 因为它允许您在 dev control@ MS K2 中查看原始请求和响应数据 。

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

```

{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}包括一个强大的 [客户端侧推演扩展名](https://github.com/bigskysoftware/htmx-extensions/tree/main/src/client-side-templates) 它允许您使用客户端@ - @ spide tableing 库将返回的 JSON 数据转换为 HTML\ @ MS K1} 这将有利于创建动态 UI, 而不必依赖服务器@ MPK2side Mading+.}

# 一些自定义扩展名

## 动态行编号

例如,在最近的一个项目中,我使用HTMX OOB 互换来更新表格中的数行“.”
为了做到这一点,我想知道表格中目前显示的行是几行 @,}所以我只更新了可见的行@.。

### 延长

```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 文件 @ ( @ avoid you@ MS K1_ re 使用模块 @ I; @ yoy 应该是\ )} 您可以做类似的事情吗?

```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 @ '@ 此扩展名很方便, 如果您使用基于查询的过滤器等@ MS K2 @ 并且您想要一些请求来保存现有的过滤器, 而其它请求则不是 @ MPK4 @ @ e @ MOSK5} @ g,} @ MIsk@ name_ '}和 @ I'_ startdate{ MSC10} 但不是 @ OMK11page+'} 或 @ MCK13sort}').}

这是SMIILAR, 与现有的 HTMX 扩展号不完全相同 [plip - 参数](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md)

### 延长

你可以看到我们勾钩 `onEvent` 收听 `htmx:configRequest` 事件@. @

然后我们 :

- 获取触发事件元素
- 获取 `preserve-params-exclude` 从元素属性 {(} {如果它存在)}\ \ 并且将其分割成一系列密钥以排除\ ( 因此我们用\ MPK3t 在请求中添加它们@)
- 从窗口位置获取当前 URL 参数
- 从请求 URL获取新参数
- 环绕当前参数并检查它们是否在排除列表中,是否已经进入新参数
- 如果它们不是,,我们就把它们加入新参数
- 最后,我们为请求的 URL 设置了新参数并返回真实状态以继续此请求@.}

```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) 标签中的标记帮助@. @ the likes `hx-controller` 和 `hx-action` 用于为您生成正确的 HTMX 属性的标签助手@ .以及 `hx-route-<x>` 用于在路由中传递的值@.}这非常有用,因为它允许您使用 C#}{}代码为属性生成正确的值,而不必在 HTML\}.}}

### 使用它

作为扩展名@ ' @%s 非常简单, 可以使用 :} @

首先,我们需要在我们的 HTMX 配置中添加扩展@. @%

```javascript

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

autload'您會注意到 預設的 HTMX 延伸使用 {'} 自行載入延伸@ .} 的方法 。

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

如果您在非“-”的环境下使用 HTMX 来应用HTMX, 这是一个很好的方法。 但是如果您在 “'” 中使用了“MS K5” 模块,你应该使用该模块 `import` 用于加载扩展名的语句,然后明确将其注册为对您的 `htmx` 例. 这只允许您利用树枝 -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` 以我们的新值替换此值, 但保存所有其他的@(}除此以外 `page` 和 `sort`@ )._ @ 这样我们就可以一直把在 URL 中设置的任何过滤器传送到服务器上, 而不必担心当我们提出新请求时这些过滤器会丢失@.} @

# ASP=.NET核心

HTMX 的精美之处之一是,它与服务器的许多互动是通过 HTTP headers @.}这些信头为服务器提供了丰富的背景信息。 这些信头让您能够从 ASP@.NET核心端点或 Razor 视图中做出适当回应。

同样,这方面的一个关键组成部分是: [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main)它提供的许多物品都很整洁。 `Request` 用于确定 HTMX 是否提出了HTMX请求,或未提出@,}并相应处理该请求。 @.

它还有自己的机制来发送触发器

```csharp

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

Push Urls etquals equist situ *%s ... etc=...* _#
Khalid做了一个伟大的工作,创建了一套扩展功能,使HTMX在ASP*.NET Core.}中易于与 HTMX合作。

在使用 HTMX 和 ASP+.NET Core. 时, 它是工具箱中的一个关键工具 [**检查一下吧,!**](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main)

## HTMX 通用HTMX请求信头

这里“ ’” 列出最有用的信头 HTMX发送的明细表, 每一份请求都加上“ I: ”

|#标题 @|}描述: #|#
|----------------------------|------------------------------------------------------------------------------------------------------------------|
|HX- 请求@|对 HTMX- 启动的请求总是设定为真实的 @.Great 用于检测中器或控制器中的HTMX呼叫@MS K5#|}
|+HX-+Target+|+DOM目标元素的代号,即该响应将被转换成 @.+Q|+#
触发此请求的元素的代号 @( @e@.g}.}一个纽扣 @MS K6_Q#|{
“|”HX-Trigger-Name |触发元素的名称 @(}用于窗体的“).|#
|+HX-+Prompt@|}包含来自 hx @-_proppt+.+#|的用户输入
请求启动时的浏览器 URL @.}对记录和上下文有用的@. @ |#
如果请求是导航后历史恢复的一部分, 请按实情发送@| @e_(# .{g}.,背按钮 @).} @MS K9}

## 请求延期

我在ASP=.NET Core Apps\.}结合HTMX}.Net这样的网络中广泛使用这些 `Request.IsHtmx()` , `Request.IsHtmxBoosted()` 和 `Request.IsHtmxNonBoosted()` 您可以很容易地检测到 HTMX 请求并相应响应@. @%

例如“,我有一个非常简单的扩展名请求, 让我可以检测一下是否某项请求针对我的主机 `#page-content` 如果是这样的话,我知道我应该寄回一个部分 .
Onte: @ 许多 donoks'} 意識到您可以指定一个 page@ '_ full page '}為部份@ MPK4} 它會跳過佈局.}

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

```

## 回应延长

除了请求扩展@ ,, 您还可以创建响应延伸来将事件发回客户端@ MPK1。 这对启动客户端侧边活动有帮助 @ MS K2

### 甜快示例

例如 [在我的SweetAlert 2整合中](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` 允许您关闭对话框@.} 您也可以使用此功能将数据传送给客户端 `HX-Trigger` header. 用于将数据从服务器传送到客户端,而不必修改 HTML 或 JavaScript 代码@.

正如你所看到的那样,通过在身体上添加一个活动听众来听这些节目很容易死。 我主要使用JSON, 因为它总是正确编码:.。

### 显示 toast

以前我写过吐司方法 [在这里](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx)@,}(但这里值得一提) 和@.}非常简单地让服务器触发客户方的敬酒通知

```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` 和 well@,}显示一个烤面包_;}再次看到更多关于它的内容 [条款内 ](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 @.}NET Core: 的两集中的一部分。

1. **[带有 ASP.NET 核心部件的 HTMX](/blog/htmx-aspnetcore-partials)** @- 聚焦于 ASP.NET核心整合@,部分观点 @MS K3}HTMX.NetMSKO5和页码
2. **本条本条** - 深潜到 HTMX 事件@, 寿命周期 @, 扩展架构_, 和自定义延伸

### 更多HTMX文章

- [添加 HTMX 的呼叫](/blog/addpagingwithhtmx) 使用 HTMX 执行射速
- [ASP.NET 核心缓存与 HTMX](/blog/aspnetcachingwithhtmx) 满足HTMX要求的缓冲战略
- [使用阿尔卑斯山和HTMX的自动-refresh](/blog/autorefreshwithalpineandhtmx) - 将阿尔卑斯山与HTMX结合用于反应组件
- [使用 HTMX 使您的站点更像 SPA @ -](/blog/htmxtomakeyoursitemorespalike) - SPA- 类似没有 JavaScript 框架的导航
- [HX 指标使用 SweetAlert](/blog/usingsweetalertforhxindicators) 使用 HTMX 的漂亮装货指标
- [使用 HTMX 显示通吐和互换](/blog/showingtoastandswappingwithhtmx) @ -_ 服务器@ MS K1_ 触发吐司通知

## 继续阅读

**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哲学的深思熟虑的文章

**圖書館&工具MS K1**

- [HTMX.NET 吉特Hub](https://github.com/khalidabuhakmeh/Htmx.Net) -HTMX.NET图书馆源代码
- [HTMX.NET NUGet](https://www.nuget.org/packages/Htmx/) @-_HTMX.NET 在 Nuget 上
- [Khalid Abuhakmeh_'网站](https://khalidabuhakmeh.com/) HTMX .NET的创建者
- [AlpineMSMK0js 文档](https://alpinejs.dev/) -官方阿尔卑斯山 MS K1js docs

**社区资源@:**

- [HTMX 差异](https://htmx.org/discord) 积极社区支持
- [超媒体系统书](https://hypermedia.systems/) 有关建立超媒体应用程序的免费在线书