Back to "HTMX扩展和与 ASP.NET 核心使用HTMX"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Core HTMX Javascript

HTMX扩展和与 ASP.NET 核心使用HTMX

Friday, 02 May 2025

一. 导言 导言 导言 导言 导言 导言 一,导言 导言 导言 导言 导言 导言

HTMX 是一个强大的 JavaScript 库, 使您能够以最小的 JavaScript 创建动态网络应用程序 。 它使您能够提出 AJAX 请求, 交换 HTML 内容, 并直接处理 HTML 属性中的事件 。 我使用HTMX大约两年了, 在这一点上和每个项目上, 我越来越了解它的能力, 更重要的是它的局限性。

但我还是不声称对它有专家知识 我只是想分享一些我在路上学到的东西

事件事件事件事件

请求准备

请求准备阶段是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 (b) 在收到请求之前修改请求;但 之后 勾勾勾 configRequest; 如 的 beforeRequest 类似的东西 HX-ValsHX-Includes 已经附在背面(在有效载荷\查询字符串中)。 你连勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾勾 htmx:afterSwap 以在内容被交换后执行操作。 与客户侧作假图书馆(例如)相结合 阿尔卑山文学 您可以在最小代码下创建强大的动态应用程序。

HTMX 提供一些内置扩展像 hx-boosthx-swap-oob 从而可以在不写任何自定义代码的情况下增强 HTMX 的功能。 然而,有时你需要建立自己的延期以满足具体要求。

例如,您可能需要在您的请求中添加自定义信头, 修改请求的有效载荷, 或者以独特的方式处理具体事件 。

实现HTMX为您提供一些方便的整合点:


{
  /**
   * 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 - 设置扩展范围并储存HTMX API的参考文献
  • 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 文件( 假设您使用模块; yoy should be) 中, 您可以做类似的事情 :

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 。 如果您使用基于查询字符串的过滤等, 此扩展名很方便 。 您想要一些请求来保存现有的过滤器, 而其他请求则不保存( 例如“ 名称” 和“ 起始日期 ”, 但不是“ page ” 或“ sort ” ) 。

这是SMIILAR, 与现有的 HTMX 扩展号不完全相同 推进参数

延长

你可以看到我们勾勾勾 onEvent 收听 htmx:configRequest 事件 。

然后我们:

  • 获取触发事件元素
  • 获取 preserve-params-exclude 从元素属性( 如果存在的话) 中从属性中分离出来, 并将其分成一系列要排除的密钥( 因此我们不把它们添加到请求中) 。
  • 从窗口位置获取当前 URL 参数
  • 从请求 URL 获取新参数
  • 环绕当前参数,检查当前参数是否在排除列表中,是否在新参数中
  • 如果不是,我们把它们加到新的参数中
  • 最后,我们为请求的 URL 设定了新的参数, 并返回真实, 以便继续请求 。
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-controllerhx-action 是为您生成正确的 HTMX 属性的标签助手。 以及 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-paramspreserve-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 将替换为我们的新值, 但保留所有其他值( 除非 pagesort) ) 所以我们可以不停地将我们在 URL 中设置的任何过滤器传递到服务器上, 而不必担心当我们提出新请求时, 它们的丢失 。

ASP.NET核心

HTMX的精美之处之一在于 它与服务器的许多互动 通过 HTTP 信头发生。 这些信头为服务器提供了内容丰富的背景信息,说明是什么触发了请求,使您能够从 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);
});

推推器等... etc... Khalid做了一个伟大的工作 创建了一系列的扩展 方便与HTMX合作 在ASP.NET核心。

这是我用HTMX和ASP.NET核心 工作的工具箱里的关键工具 检查出来!

HTMX 通用 HTMX 请求信头

以下是最有用的信头 HTMX 发送的所有请求的细目 :

  • 标题 * * 描述 * |----------------------------|------------------------------------------------------------------------------------------------------------------|
  • HX-Request 总是对 HTMX 发起的任何请求做出正确规定。 用于检测中器或控制器中 HTMX 呼叫功能的绝佳功能。 _____________________________________________________________________________________________________________________________________________________________________________________________ HX-Target * DOM中目标元素的代号,该响应将被转换成 。 _____________________________________________________________________________________________________________________________________________________________________________________________
  • HX-Trigger * 触发请求的因素的代号(例如: (a) 按钮)。 _____________________________________________________________________________________________________________________________________________________________________________________________ HX-Triger-Name 触发元素的名称(可用于窗体)。 _____________________________________________________________________________________________________________________________________________________________________________________________ HX-Prompt 包含来自 hx-prompt 的用户输入 。 _____________________________________________________________________________________________________________________________________________________________________________________________ 请求启动时的浏览器 URL URL 。 对伐木和背景情况有用。 _____________________________________________________________________________________________________________________________________________________________________________________________
  • HX-History-Restore-Restore-Request {{{{{{}}}}如果请求是航行后历史恢复的一部分(例如背按钮),则该请求是真实的。 _____________________________________________________________________________________________________________________________________________________________________________________________

请求延期

我在ASP.NET核心应用软件中广泛使用这些软件。 与HTMX.Net等 Request.IsHtmx() , Request.IsHtmxBoosted()Request.IsHtmxNonBoosted() 您可以很容易地检测到HTMX要求并做出相应反应。

例如,我有一个非常简单的扩展名 请求,它让我可以检测 是否请求针对我的主 #page-content (div) div. 如果那样的话,我知道我应该寄回一部分 注意: 许多人不知道你可以指定一个“ 全页”为部分, 然后跳过布局 。

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

回应延长

除请求延期外,您还可以创建响应延期,将事件发回客户。 这有助于启动客户附带活动。

甜快示例

例如 在我的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 因为它总是正确的编码。

显示汤点

以前我写过吐司方法 在这里,但这里也值得一提。 简单地让服务器在客户端触发敬酒通知 我设置了触发器 在这个反应延长。

    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 函数和 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);
}

在结论结论中

就是这样 HTMX和ASP.NET核心的旋风巡演 我希望你认为它有用和内容丰富。 如有任何问题或意见,请自由评论如下。

logo

©2024 Scott Galloway