Back to "使用 HTMX( 以及 ASP. NET 核心) 显示通缩和互换内容"

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

使用 HTMX( 以及 ASP. NET 核心) 显示通缩和互换内容

Saturday, 12 April 2025

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

HTMX是一个伟大的图书馆,可以使您的网络应用程序更具活力和响应性。 我将教你们如何使用HTMX在页面上展示敬酒通知和互换内容。

标准 HTMX 中的“限制”之一(即: 非 OOB 互换通常只有从后端交换的单部分内容。 然而,通过使用 HX-Trigger 头头和一个小刺绣。

注:您可以使用 hx-swap-oob 转换两个不同的 内容内容内容 元素,但这个元素比较复杂, 做一些 JavaScripty 的东西并不容易使用。

东 多

我一直在使用这个简单的吐吐司通知系统的变种 暂时的.. 这是一个简单的函数, 需要信息、 持续时间和输入( 成功、 错误、 警告) 并在页面上显示敬酒通知 。

这个“ 最新版本” 围绕图标、 动画等等,

Javastrash 标注

// HTMX toast notification
// Simple HTMX toast handler for use with hx-on::after-request
window.showToast = (message, duration = 3000, type = 'info') => {
    const toast = document.getElementById('toast');
    const toastMessage = document.getElementById('toast-message');
    const toastText = document.getElementById('toast-text');
    const toastIcon = document.getElementById('toast-icon');

    // Reset classes
    toastMessage.className = 'alert shadow-lg gap-2 transition-all duration-300 ease-in-out cursor-pointer';
    toastIcon.className = 'bx text-2xl';

    // Add DaisyUI alert type
    const alertClass = `alert-${type}`;
    toastMessage.classList.add(alertClass);

    // Add icon class
    const iconMap = {
        success: 'bx-check-circle',
        error: 'bx-error-circle',
        warning: 'bx-error',
        info: 'bx-info-circle'
    };
    const iconClass = iconMap[type] || 'bx-bell';
    toastIcon.classList.add(iconClass);

    // Set the message
    toastText.textContent = message;

    // Add slide-in animation
    toastMessage.classList.add('animate-slide-in');
    toast.classList.remove('hidden');

    // Allow click to dismiss
    toastMessage.onclick = () => hideToast();

    // Auto-dismiss
    clearTimeout(window.toastTimeout);
    window.toastTimeout = setTimeout(() => hideToast(), duration);

    function hideToast() {
        toastMessage.classList.remove('animate-slide-in');
        toastMessage.classList.add('animate-fade-out');
        toastMessage.onclick = null;

        toastMessage.addEventListener('animationend', () => {
            toast.classList.add('hidden');
            toastMessage.classList.remove('animate-fade-out');
        }, { once: true });
    }
};

这使用一个小 HTML 片段, 我定义在 _布局. cshtml 文件( 使用我首选的尾风 CSS & DaisiUI) 。 注意结尾处的“阶级保护区块”。 这是一个小把戏,以确保在最后的 HTML 输出中保存分类 。 这真的是为了我的尾风布局,我只看一眼 cshtml.

<div
        id="toast"
        class="toast toast-bottom fixed z-50 hidden w-full md:w-auto max-w-sm right-4 bottom-4"
>
    <div
            id="toast-message"
            class="alert shadow-lg gap-2 transition-all duration-300 ease-in-out cursor-pointer"
    >
        <i id="toast-icon" class="bx text-2xl"></i>
        <span id="toast-text">Notification message</span>
    </div>
</div>

<!-- class-preserving dummy block -->
<div class="hidden">
    <div class="alert alert-success alert-error alert-warning alert-info"></div>
    <i class="bx bx-check-circle bx-error-circle bx-error bx-info-circle bx-bell"></i>
    <div class="animate-slide-in animate-fade-out"></div>
</div>

反尾风

这里我定义了“ 树木摇晃” 的文件, 并定义了吐司使用的一些动画类 。

const defaultTheme = require("tailwindcss/defaultTheme");

module.exports = {
  content: ["./Views/**/*.cshtml", "./Areas/**/*.cshtml"],
  safelist: ["dark"],
  darkMode: "class",
  theme: {
    extend: {
      keyframes: {
        'slide-in': {
          '0%': { opacity: 0, transform: 'translateY(20px)' },
          '100%': { opacity: 1, transform: 'translateY(0)' },
        },
        'fade-out': {
          '0%': { opacity: 1 },
          '100%': { opacity: 0 },
        },
      },
      animation: {
        'slide-in': 'slide-in 0.3s ease-out',
        'fade-out': 'fade-out 0.5s ease-in forwards',
      },
  },
  plugins: [require("daisyui")],
};

触发

秘密 使这一切的 工作是使用 HTMX 触发信头功能.

"通常"现在你会 在您 html / 剃须刀代码中定义此定义:

<div hx-get="/clicked" hx-trigger="click[ctrlKey]">Control Click Me</div>

或者您可以在请求后的事件中定义它 。 所以,你做一些事情 然后它触发了一个新的事件。

<button 
    hx-get="/api/do-something"
    hx-swap="none"
    hx-on::afterRequest="window.showToast('API call complete!', 3000, 'success')"
    class="btn btn-primary"
>
    Do Something
</button>

如果你只想做点什么 那就表明已经做了 但我想换掉一些内容 举杯

            Response.Headers.Append("HX-Trigger", JsonSerializer.Serialize(new
            {
                showToast = new
                {
                    toast = result.Message,
                    issuccess = result.Success
                }
            }));

如果我的触发器被点名 showToast 我传递信息 和成功旗。 所以我的联署材料 我为这次活动指定了一个活动听众 然后,这等于是说: showToast 和成功标记中的传递功能。

// Handles HX-Trigger: { "showToast": { "toast": "...", "issuccess": true } }
document.body.addEventListener("showToast", (event) => {
    const { toast, issuccess } = event.detail || {};
    const type = issuccess === false ? 'error' : 'success';
    showToast(toast || 'Done!', 3000, type);
});

ASP.NET

我为什么要用这个? 在最近的一个工作项目中,我想对一个在表格上展示的用户采取一些行动。 我想展示一个敬酒会通知 并用新内容交换用户行的内容

userrow.png

正如你可以看到的,我有一个BUNCH按钮,它“做一些事情”给用户。 我想展示一个敬酒会通知 并用新内容交换用户行的内容

所以在我的控制下,我有一个简单的“开关”, 它使用动作名称, 做一些东西, 然后返回新的请求结果 。

    private async Task ApplyAction(string email, string useraction)
    {
        if (!string.IsNullOrWhiteSpace(useraction) &&
            Enum.TryParse<UserActionType>(useraction, true, out var parsedAction))
        {
            RequestResult result;

            switch (parsedAction)
            {
                case UserActionType.FlipRoles:
                    result = await userActionService.FlipRestaurantPermissions(email);
                    break;
                case UserActionType.UnflipRoles:
                    result = await userActionService.UnFlipRestaurantPermissions(email);
                    break;
                case UserActionType.Enable2FA:
                    result = await userActionService.ToggleMFA(email, true);
                    break;
                case UserActionType.Disable2FA:
                    result = await userActionService.ToggleMFA(email, false);
                    break;~~~~
                case UserActionType.RevokeTokens:
                    result = await userActionService.RevokeTokens(email);
                    break;
                case UserActionType.Lock:
                    result = await userActionService.Lock(email);
                    break;
                case UserActionType.Unlock:
                    result = await userActionService.Unlock(email);
                    break;
                case UserActionType.Nuke:
                    result = await userActionService.Nuke(email);
                    break;
                case UserActionType.Disable:
                    result = await userActionService.DisableUser(email);
                    break;
                case UserActionType.Enable:
                    result = await userActionService.EnableUser(email);
                    break;
                case UserActionType.ResetPassword:
                    result = await userActionService.ChangePassword(email);
                    break;
                case UserActionType.SendResetEmail:
                    result = await userActionService.SendResetEmail(email);
                    break;
                default:
                    result = new RequestResult(false, "Unknown action");
                    break;
                  
            }

            Response.Headers.Append("HX-Trigger", JsonSerializer.Serialize(new
            {
                showToast = new
                {
                    toast = result.Message,
                    issuccess = result.Success
                }
            }));

        }
    }

你可以看到我还附上了 HX-Trigger 响应的页眉。 这是JSON的物体 showToast 键和带有 toastissuccess 键。 缩略 toast 键是要在敬杯通知和 issuccess 键是显示该动作是否成功的布尔。

然后在 _Row 我部分拥有 HX (使用 HTMX. Net) 属性来触发动作 。

                     <!-- Revoke Login Tokens -->
                            <button class="btn btn-xs btn-error border whitespace-normal text-wrap tooltip tooltip-left" data-tip="Revoke login tokens"
                                    hx-get hx-indicator="#loading-modal" hx-target="closest tr" hx-swap="outerHTML"
                                    hx-action="Row" hx-controller="Users"
                                    hx-route-email="@user.Email" hx-route-useraction="@UserActionType.RevokeTokens"
                                    hx-confirm="Are you sure you want to revoke the login tokens for this user?">
                                <i class="bx bx-power-off"></i> Revoke
                            </button>

你可以看到我用的是目标 closest tr 将整个行与新内容互换。 这是更新行内容的简单方式, 不必做整页刷新 。

部分视图

对于ASP.NET核心和HTMX来说,这是非常简单和伟大的技术。 您可以选择使用 HTMX.Nets 但在这种情况下,我只用HTMX回调的这个

    [Route("row")]
 
    public async Task<IActionResult> Row(string email, string? useraction = null)
    {

        if(!string.IsNullOrEmpty(useraction))
          await ApplyAction(email, useraction);

        var userRow = await userViewService.GetSingleUserViewModel(email);
        return PartialView("_Row", userRow);
    }

在这种情况下,部分意见 _Row 是一个简单的表格行,带有用户信息和要执行动作的按钮。

额外的HTMX特性

我还使用另外几个HTMX功能,使用户体验更好。

正在装入

我也用一个简单的 loading modal 说明请求正在执行中。 这是向用户展示背景中正在发生某事的简单方式。

<div id="loading-modal" class="modal htmx-indicator">
    <div
        class="modal-box flex flex-col items-center justify-center bg-base-200 border border-base-300 shadow-xl rounded-xl text-base-content dark text-center ">
        <div class="flex flex-col items-center space-y-4">
            <h2 class="text-lg font-semibold tracking-wide">Loading...</h2>
            <span class="loading loading-dots loading-xl text-4xl text-stone-200"></span>
        </div>
    </div>
</div>

确认

我也使用 hx-confirm 属性可在执行动作前显示确认对话框。 这是确保用户真正想要执行该动作的简单方法。 此用途 甜加速器2color 显示确认对话框。

如果您不这样做, HTMX 仍然有效, 但是它使用标准的浏览器“ confirm” 对话框, 这对用户来说有点困难 。

// HTMX confirm with SweetAlert2
window.addEventListener('htmx:confirm', (e) => {
    const message = e.detail.question;
    if (!message) return;

    e.preventDefault();

    Swal.fire({
        title: 'Please confirm',
        text: message,
        icon: 'warning',
        showCancelButton: true,
        confirmButtonText: 'Yes',
        cancelButtonText: 'Cancel',
        theme: 'dark',
    }).then(({ isConfirmed }) => {
        if (isConfirmed) e.detail.issueRequest(true);
    });
});

在结论结论中

这是使用 HTMX 在页面上显示吐司通知和交换内容的简单方法 。 这是让您的网络应用程序更具活力和响应性的伟大方法。

logo

©2024 Scott Galloway