# Автозавершений частково оновлювач з альпійськими.js і HTMX

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

<!--category-- Javascript, HTMX, Alpine.js, ASP.NET Core -->
# Вступ

Таким чином, в проекті, в якому я хочу додати можливість для часткової частини автонастанови на шкалі часу.

Ось як я це зробив, використовуючи альпійські.js і HTMX.

[TOC]

# Вимоги

Я хотів, щоб це було

1. Можливий для повторного використання; отже, він має бути простим і самодостатнім для автоматичного визначення будь- якого елемента.
2. Вона повинна поважати існуючі параметри Url
3. Сторону сервера слід виявити (у ядрах ASP. NET у цьому випадку)
4. Якщо увімкнено, то це повинно бути увімкнено *для цієї кінцевої точки* Тільки і це слід запам' ятати між запитами.
5. Цей пункт має негайно виконувати оновлення, якщо його увімкнено (так, що користувач знає, як він виглядає)
6. Користувач може вимкнути цей режим
7. Простіше включити сторінку.

З цим на увазі я почав будувати маленький модуль JS за допомогою альпійських.js і HTMX.

## ЗАУВАЖЕННЯ

Ви можете виконувати автоматичне оновлення можливостей " on і off " та " Пам' ятати " досить просто з самим 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>
```

Дякую. [@ KhalidAbuhkme](https://khalidabuhakmeh.com/) за вказування цього.

Цей код насправді використовується `hx-trigger` щоб налаштувати автодату. Просто використовуйте альпійські. js для налаштування атрибутів HTMX.

Це насправді додає клієнтську сторону взаємодії користувача; це те, що альпійські.js є чудовим.

# Код

Код цього коду дійсно досить компактний, він розбитий на дві основні частини: модуль JS, оглядачі подій і HTML.

## Модуль

Цей модуль є простим модулем JS, який використовує альпійські. 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()` Метод

За допомогою цього способу можна увімкнути або вимкнути автоматичне опитування елемента HTML за допомогою HTMX.

### Поведінка

- Вибирає елемент за ним `endpointId`.
- Зібрати адресу URL запиту (`fullUrl`) об'єднавши дані `actionUrl` з рядком запиту поточної сторінки.
- Перевіряє, чи було попередньо увімкнено опитування читанням з `localStorage` (добре, що він запам'ятався між сеансами перегляду)

#### КОЛИ `this.autoUpdate` є `true` (i.e., опитування увімкнено):

- Встановлює атрибути 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` (i.e., опитування вимкнено):

- Вилучає перелічені вище атрибути 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 _)` И если он там, я знаю, что это просьба с автоматами.

Потім я беру поточний Йіме (це для французького клієнта, тому я конвертую в Париж) і з часом показую тост.

### ShowToast

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

```

Це викликає мій тост компонент I. [пишеться про це ](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx).

### Заряджаючи його

Дуже просто з' єднати цей модуль, у вашій головній.js\ index.js, незалежно від того, чи просто імпортувати модуль і під'єднати його до вікна Window

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

```

Потім ми називаємо метод init в коді Razor ASP.NET:

## Код Razor ASP. NET

Зробити це як маленький і придатний як можливо код Razor досить простий.

Тут ви можете бачити, що ви можете вказати атрибути даних альпійських. js, які слід налаштувати автоматично.

- Дані x: тут ми встановлюємо об' єкт даних альпійських.js.
- x-init: тут ми називаємо метод ініціалізації контролера автооновлення.
- x- on: change: тут ми називаємо режим автооновлення для контролера автооновлення.
- ІД- кінцевої точки_ даних: це ідентифікатор елемента, який слід оновити.
- url- data- action: це адреса url, яку слід викликати для оновлення елемента.
- Interval data: це проміжок часу, який буде використано для автоматичної дати (типово, 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>
```

# Включення

І це все, досить просто. Learching HTMX і Billian.js для створення простого автоматичного компонента, який можна легко використовувати з ядра ASP.NET.