# Autopäivitä osittainen päivitys Alpine.js- ja HTMX-laitteilla

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

<!--category-- Javascript, HTMX, Alpine.js, ASP.NET Core -->
# Johdanto

Joten siinä, mitä on tulossa sarjaksi, halusin työprojektissa lisätä mahdollisuuden osittaiseen automaattiseen päivitykseen tietyllä aikataululla.

Näin tein sen Alpine.js:n ja HTMX:n avulla.

[TOC]

# Vaatimukset

Joten halusin, että tämä olisi

1. Uudelleenkäytettävä, joten sen pitäisi olla yksinkertainen ja omavarainen niin, että se voi automaattisesti päivittää minkä tahansa elementin.
2. Siinä olisi otettava huomioon nykyiset Url-parametrit.
3. Sen pitäisi olla havaittavissa Server Side (ASP.NET Core tässä tapauksessa)
4. Jos se kytketään päälle, se tulee ottaa käyttöön *kyseiselle päätetapahtumalle* vain ja tämä pitäisi muistaa pyyntöjen välillä.
5. Sen pitäisi tehdä päivitys heti, kun se otetaan käyttöön (joten käyttäjä tietää, miltä se näyttää)
6. Käyttäjän pitäisi pystyä kytkemään se pois päältä
7. Sivulle pitäisi olla helppo lisätä.

Tätä silmällä pitäen ryhdyin rakentamaan pientä JS-moduulia Alpine.js:n ja HTMX:n avulla.

## HUOMAUTUS

Voit tehdä automaattipäivityksiä "on and off" ja "muistaa" -ominaisuuksista yksinkertaisesti pelkästään HTMX:n avulla.
Esimerkiksi käyttämällä [HTMX-lähettimet ](https://htmx.org/attributes/hx-trigger/) voi todella tehdä paljon juttuja.

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

Kiitos [@KhalidAbuhakmeh](https://khalidabuhakmeh.com/) Tästä huomautuksesta.

Tämä koodi itse asiassa käyttää `hx-trigger` Auto-update-järjestelmän perustaminen. Vain Alpine.js:n avulla määrittää HTMX-ominaisuuksia.

Tämä todella lisää käyttäjien vuorovaikutusta asiakaspuolella, jossa Alppi.js on hyvä.

# Koodi

Koodi on todella aika kompakti, se on jakautunut kahteen pääosaan: JS-moduuliin, tapahtumakäsittelijöihin ja HTML:ään.

## Moduuli

Moduuli on yksinkertainen JS-moduuli, joka käyttää Alppien.js-moduulia automaattipäivityksen tilan hallintaan. Se käyttää paikallista tallennustilaa muistaakseen pyyntöjen välisen automaattisen päivityksen tilan.

Se hyväksyy paramit:

- `endpointId` - päivitettävän elementin tunniste
- `actionUrl` - url tulee kutsua päivittämään elementtiä
- `initialInterval` - automaattipäivityksessä käytettävä alkuväli (oletus on 30 sekuntia)

Näemme myös, että se käyttää paria avainta, joita käytetään paikalliseen säilytykseen muistamaan automaattipäivityksen tila.
Huomaat, että käytän `actionurl` Osana avainta, jolla tämä päätetapahtuma voidaan tehdä erityiseksi.

```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()` Menetelmä

Tämä menetelmä mahdollistaa HTMX:n avulla kohde-HTML-elementin automaattisen äänestämisen tai poistaa sen käytöstä.

### Käyttäytyminen

- Valitsee elementin sen avulla `endpointId`.
- Koostaa pyynnön URL-osoitteen (`fullUrl`) yhdistämällä `actionUrl` Nykyisen sivun kyselyjousella.
- Tarkistukset, jos äänestys oli aiemmin käytössä lukemalla `localStorage` (hyvä niin kuin se muistetaan selainsessioiden välillä).

#### Milloin?.................................................................................... `this.autoUpdate` is `true` (ts. äänestys on sallittu):

- Aseta HTMX- attribuutit elementille:
  - `hx-trigger` mielipidemittaukseen joka `interval` Sekunnit
  - `hx-swap="innerHTML"` Elementin sisällön korvaaminen
  - `hx-get` Osoittaa äänestysten URL-osoitteen
  - `hx-headers` custom-ohjelman lisääminen `"AutoPoll": "auto"` Otsikko
- Tallentaa käytössä olevan tilan `localStorage`
- Puhelut `htmx.process(el)` antaa HTMX:n tunnistaa uudet ominaisuudet
- Jos se oli aiemmin pois päältä, se käynnistää välittömästi HTMX-pyynnön kautta `htmx.ajax()` (ei ole riippuvainen HTMX-tapahtumajohdoista)

#### Milloin?.................................................................................... `this.autoUpdate` is `false` (ts. äänestys on keskeytetty):

- Poistaa yllä olevat HTMX- attribuutit
- Tyhjentää tallennetun asetuksen `localStorage`
- Puhelut `htmx.process(el)` jälleen HTMX-käyttäytymisen päivitykseen

### Automaattinen kysely, kun se otetaan käyttöön

Meillä on täällä myös haara, joka suorittaa automaattipollin, kun se otetaan käyttöön.

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

Tämä tekee HTMX-pyynnön `fullUrl` ja päivittää kohde-elementtiä vastauksella. Tästä on hyötyä, kun käyttäjälle näytetään, miltä automaattipäivitys näyttää, kun hän ottaa sen käyttöön.

## Otsikko

Huomaat, että lähetämme pyynnön mukana myös HTMX-otsikon. Tämä on tärkeää, koska sen avulla voimme havaita pyyntöpalvelimen puolen.

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

```

Palvelinpuolellani havaitsen sitten, että tämä otsikko on asetettu

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

Huomaat, että minä vain etsin päällimmäistä `Request.Headers.TryGetValue("AutoPoll", out _)` Ja jos se on siellä, tiedän, että se on automaattipollipyyntö.

Sitten nappaan nykyisen yimen (se on ranskalaiselle asiakkaalle, joten käännyn Pariisin aikaan) ja nostan maljan ajan kanssa.

### Näytä toast

Erytropoietiini `ShowToast` Method on yksinkertainen laajennusmenetelmä, joka laukaisee HTMX-viestin näyttämisen.

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

    }
```

Sen jälkeen HTMX:n paahtoleipäkomponenttini, joka näyttää viestin, havaitsee sen.

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

```

Tämä sitten kutsuu minun Paahtoleipä komponentti I [kirjoitti tästä ](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx).

### Kytketään se kiinni

Moduuli on aika helppo kytkeä pääosiin.js \ index.js mikä tahansa tuo sen ja kytkee sen ikkunaan.

```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-koodin init-menetelmää kutsutaan tämän jälkeen seuraavasti:

## ASP.NET Razor-koodi

Jotta tämä olisi mahdollisimman pieni ja käyttökelpoinen, Razor-koodi on melko yksinkertainen.

Tässä näet Määritän Alppien.js-datan ominaisuudet automaattisen päivityksen määrittämiseksi.

- X-data: Tähän olemme perustaneet Alppien.js-dataobjektin.
- X-init: Tässä kohtaa kutsumme automaattipäivitysohjaimeen init-menetelmää.
- x-on:change: Tässä kohtaa kutsumme automaattipäivitysmenetelmää automaattipäivitysohjaimella.
- Data-endpoint-id: Tämä on päivitettävän elementin tunniste.
- Data-action-url: Tämä on url, jota pyydetään päivittämään elementti.
- Tietojen väliaika: Tämä on automaattipäivityksessä käytettävä aikaväli (oletus on 30 sekuntia).

Huomaat, että asetamme tavoitteeksi käyttää pyyntöä `campaignemail-request-list` elementti. Tämä on elementti, jota päivitetään uudella sisällöllä.
Sitten sivulle on lisätty SOMWHERE.

Nyt kun valintaruutu on valittu, se päivittää listan automaattisesti 30 sekunnin välein.

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

# Johtopäätöksenä

Siinä kaikki, aika yksinkertainen oikein. HTMX:n ja Alpine.js:n lainaaminen yksinkertaisen automaattisen päivityskomponentin luomiseksi ASP.NET Coresta helposti.