# 与Alpine.js和HTMX的自动更新部分更新器

<datetime class="hidden">2025-04-23T19:30</datetime>

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

所以在一系列的系列中,在一个工作项目中, 我想增加部分能力, 以自动更新特定的时间尺度。

这是我用阿尔卑斯山和HTMX做的

[TOC]

# 所需资源所需资源所需资源所需资源所需资源所需资源所需资源所需资源所需资源所需资源所需资源

所以我希望这个

1. 可再使用; 因此它应该简单且自成一体, 足以自动更新任何元素 。
2. 它应尊重现有的Url参数
3. 它应该是可探测的服务器侧(在这种情况下为ASP.NET核心)
4. 如果打开它, 应该启用它 。 *该终点的终点* 只有在请求之间才能记住这一点。
5. 一旦启用,它应立即更新(让用户知道它长什么样)
6. 用户应能够关掉它
7. 列入一页应该很简单。

考虑到这一点,我准备用阿尔卑斯山和HTMX建立一个小型的联署材料模块。

## 注 注 注 注 注注

您可以用自动更新“ 运行和关闭” 和“ 记住” 功能, 只需使用 HTMX 即可 。
例如; 例如; 使用 [HTMX 触发器 ](https://htmx.org/attributes/hx-trigger/) 你真的可以做很多事情。

```html

<div id="campaignemail-request-list" hx-get="@Url.Action("List", "CampaignEmailRequest")" hx-trigger="every 30s" hx-swap="innerHTML">
    <partial name="_List" model="@Model"/>
</div>
```

感谢 [@KhalidAbuhakmeh 写道:](https://khalidabuhakmeh.com/) 指出这一点。

此代码实际上确实使用此代码 `hx-trigger` 设置自动更新。 使用 Alpine.js 配置 HTMX 属性。

这真正增加的是用户互动客户端;这就是阿尔卑斯山的伟大之处。

# 《刑法典》

这个代码非常紧凑, 分为两个主要部分: JS模块,事件处理者和 HTML。

## 模块模块

该模块是一个简单的联署材料模块,使用Alpine.js管理自动更新状态。 它使用本地存储系统来记起在两次请求之间自动更新的状态。

它接受以下参数:

- `endpointId` - 需要更新的内容的代号
- `actionUrl` - 需要调用URL来更新元素
- `initialInterval` - 自动更新所使用的初始间隔(默认为 30 秒)

我们还可以看到它使用几个密钥;这些密钥用于当地的存储,以便记住自动更新的状态。
你可以看到我用的是 `actionurl` 作为使这一终点具体化的关键部分。

```javascript
export function autoUpdateController(endpointId, actionUrl, initialInterval = 30) {
const keyPrefix = `autoUpdate:${actionUrl}`;
const enabledKey = `${keyPrefix}:enabled`;

    return {
        autoUpdate: false,
        interval: initialInterval,

        toggleAutoUpdate() {
            const el = document.getElementById(endpointId);
            if (!el) return;

            const url = new URL(window.location.href);
            const query = url.searchParams.toString();
            const fullUrl = query ? `${actionUrl}?${query}` : actionUrl;

            const wasPreviouslyEnabled = localStorage.getItem(enabledKey) === 'true';

            if (this.autoUpdate) {
                el.setAttribute('hx-trigger', `every ${this.interval}s`);
                el.setAttribute('hx-swap', 'innerHTML');
                el.setAttribute('hx-get', fullUrl);
                el.setAttribute('hx-headers', JSON.stringify({ AutoPoll: 'auto' }));

                localStorage.setItem(enabledKey, 'true');

                htmx.process(el); // rebind with updated attributes
                
                if (!wasPreviouslyEnabled) {
                    htmx.ajax('GET', fullUrl, {
                        target: el,
                        swap: 'innerHTML',
                        headers: {AutoPoll: 'auto'}
                    });
                }
            } else {
                el.removeAttribute('hx-trigger');
                el.removeAttribute('hx-get');
                el.removeAttribute('hx-swap');
                el.removeAttribute('hx-headers');

                localStorage.removeItem(enabledKey);
                htmx.process(el);
            }
        },

        init() {
            this.autoUpdate = localStorage.getItem(enabledKey) === 'true';
            this.toggleAutoUpdate();
        }
    };
}
```

### `toggleAutoUpdate()` 方法方法方法

此方法能够或禁用使用 HTMX 对目标 HTML 元素进行自动投票 。

### 行为行为

- 根据其 `endpointId`.
- 构建请求 URL ()`fullUrl`合并给定的 `actionUrl` 使用当前页面的查询字符串。
- 检查先前是否通过读取 `localStorage` (在浏览器会话之间可以记住)

#### 何时 `this.autoUpdate` 是 `true` (即使投票得以进行):

- 设置元素上的 HTMX 属性 :
  - `hx-trigger` 民意调查 `interval` 秒数秒
  - `hx-swap="innerHTML"` 替换元素的内容
  - `hx-get` 指向投票 URL
  - `hx-headers` 添加自定义 `"AutoPoll": "auto"` 标题标题标题
- 保存启用状态 `localStorage`
- 呼吁呼吁 `htmx.process(el)` 使 HTMX 识别新属性
- 如果以前曾关闭过,应立即通过HTMX `htmx.ajax()` (不依赖 HTMX 事件电线)

#### 何时 `this.autoUpdate` 是 `false` (即投票有残疾):

- 删除上述 HTMX 属性
- 清除保存的设置 `localStorage`
- 呼吁呼吁 `htmx.process(el)` 以再次更新 HTMX 行为

### 第一次启用时自动投放

我们这里还有一个分行 第一次启用自动投币时执行自动投币

```javascript
const wasPreviouslyEnabled = localStorage.getItem(enabledKey) === 'true';
      if (!wasPreviouslyEnabled) {
                    htmx.ajax('GET', fullUrl, {
                        target: el,
                        swap: 'innerHTML',
                        headers: {AutoPoll: 'auto'}
                    });
                }
```

执行 HTMX 向 `fullUrl` 并根据回应更新目标要素。 用于显示用户在启用时自动更新的外观 。

## 页眉

你将会注意到,我们也发送一个HTMX信头 与请求。 这一点很重要,因为它使我们能够检测请求服务器的侧面。

```javascript
   el.setAttribute('hx-headers', JSON.stringify({ AutoPoll: 'auto' }));
headers: {AutoPoll: 'auto'}

```

在我的服务器侧端, 我检测到这个信头是使用

```csharp
 if (Request.Headers.TryGetValue("AutoPoll", out _))
        {
            
            
            var utcDate = DateTime.UtcNow;
            var parisTz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Paris");
            var parisTime = TimeZoneInfo.ConvertTimeFromUtc(utcDate, parisTz);

            var timeStr = parisTime.ToString("yyyy-MM-dd HH:mm:ss");
              Response.ShowToast($"Auto Update Last updated: {timeStr} (paris)",true); 
         
            return PartialView("_List", requests);
        }
```

你等着瞧,我只要找找头头和 `Request.Headers.TryGetValue("AutoPoll", out _)` 如果它在那里,我知道 这是一个自动投票请求。

然后拿下现在的 Yime (这是给法国客户的, 所以我改换巴黎时间) 并用时间举杯祝酒。

### 显示汤点

缩略 `ShowToast` 方法是一种简单的扩展方法,它设定一个触发器,让 HTMX 显示一个敬杯信息。

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

    }
```

然后通过我的HTMX吐司组件 检测到它 显示信息。

```javascript
document.body.addEventListener("showToast", (event) => {
    const { toast, issuccess } = event.detail || {};
    const type = issuccess === false ? 'error' : 'success';
    showToast(toast || 'Done!', 3000, type);
});

```

然后,这需要我的吐司构成部分一。 [在此写入 ](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx).

### 勾搭起来

将这个模块连接起来非常简单, 在您的主,js\ index.js 中,不管什么只是导入它,然后把它连接到窗口

```javascript
import './auto-actions';

window.autoUpdateController = autoUpdateController; //note this isn't strictly necessary but it makes it easier to use in the HTML


//Where we actually hook it up to Alpine.js
document.addEventListener('alpine:init', () => {
    Alpine.data('autoUpdate', function () {
        const endpointId = this.$el.dataset.endpointId;
        const actionUrl = this.$el.dataset.actionUrl;
        const interval = parseInt(this.$el.dataset.interval || '30', 10); // default to 30s

        return autoUpdateController(endpointId, actionUrl, interval);
    });
});

```

然后我们调用ASP.NET Razor代码中的Init方法:

## ASP.NET Razor 代码

尽可能小的和可以重复使用 剃刀代码很简单

您可以在这里看到, 我指定了 Alpine.js 数据属性来设置自动更新 。

- x- data: 这是我们设置阿尔卑斯山.js 数据对象的地方。
- X- init: 这是我们在自动更新控制器上调用输入方法的地方 。
- X on: change: 这是我们在自动更新控制器上调用 ggleAutoUnited 方法的地方 。
- 数据- 结束点- id: 这是要更新的元素的代号 。
- 数据- 动作- URL: 这是用来更新元素的 URL 。
- 数据interval: 这是自动更新使用的间隔( 默认为 30 秒 ) 。

你会看到我们设定了目标 用于请求 `campaignemail-request-list` 元素。 此元素将随新内容更新 。
然后在页面上包括了某个地方。

当选中一个复选框时, 它会自动每30秒更新一次列表 。

```html
            <div class=" px-4 py-2 bg-base-100 border border-base-300 rounded-box"
                x-data="autoUpdate()" 
                x-init="init"
                x-on:change="toggleAutoUpdate"
                data-endpoint-id="campaignemail-request-list"
                data-action-url="@Url.Action("List", "CampaignEmailRequest")"
                data-interval="30"
                >
                <label class="flex items-center gap-2">
                    <input type="checkbox" x-model="autoUpdate" class="toggle toggle-sm" />
                    <span class="label-text">
                        Auto update every <span x-text="$data.interval"></span>s
                    </span>
                </label>
            </div>

        <!-- Voucher List -->
        <div id="campaignemail-request-container">
            <div
                id="campaignemail-request-list">
                <partial name="_List" model="@Model"/>
            </div>
        </div>
```

# 在结论结论中

就是这样,非常简单的权利。 利用 HTMX 和 Alpine.js 创建一个简单的自动更新组件, 我们很容易从 ASP. NET Core 中使用 。