Aggiornamento parziale automatico con Alpine.js e HTMX (Italiano (Italian))

Aggiornamento parziale automatico con Alpine.js e HTMX

Comments

NOTE: Apart from English (and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.

Wednesday, 23 April 2025

//

Less than a minute

Introduzione

Così in quella che sta diventando una serie, in un progetto di lavoro ho voluto aggiungere la capacità per un parziale di auto-aggiornamento su un dato lasso di tempo.

Ecco come l'ho fatto usando Alpine.js e HTMX.

Requisiti

Quindi volevo che questo fosse...

  1. riutilizzabile; quindi dovrebbe essere semplice e auto-contenuto abbastanza da auto-aggiornare qualsiasi elemento.
  2. Dovrebbe rispettare i parametri Url esistenti
  3. Dovrebbe essere il lato del server rilevabile (in ASP.NET Core in questo caso)
  4. Se acceso dovrebbe essere abilitato per quell'endpoint solo e questo dovrebbe essere ricordato tra le richieste.
  5. Dovrebbe immediatamente eseguire l'aggiornamento quando abilitato (in modo che l'utente sappia come appare)
  6. Dovrebbe essere in grado di essere spento dall'utente
  7. Dovrebbe essere semplice da includere in una pagina.

Con questo in mente ho deciso di costruire un piccolo modulo JS utilizzando Alpine.js e HTMX.

NOTA

È possibile fare auto aggiornamenti 'on e off' e 'ricorda' caratteristiche abbastanza semplicemente con HTMX da solo. Per esempio; usando Trigger HTMX Puoi davvero fare un sacco di cose.


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

Grazie a @KhalidAbuhakmeh Per averlo fatto notare.

Questo codice di fatto usa hx-trigger per impostare l'aggiornamento automatico. Basta usare Alpine.js per configurare gli attributi HTMX.

Ciò che questo aggiunge realmente è il lato cliente di interazione dell'utente; che è che cosa Alpine.js è grande in.

Il codice

Il codice per questo è davvero piuttosto compatto, è suddiviso in due parti principali: un modulo JS, i gestori di eventi e l'HTML.

Il modulo

Il modulo è un semplice modulo JS che utilizza Alpine.js per gestire lo stato dell'aggiornamento automatico. Usa lo storage locale per ricordare lo stato dell'aggiornamento automatico tra le richieste.

Accetta i parami:

  • endpointId - l'id dell'elemento da aggiornare
  • actionUrl - l'url da chiamare per aggiornare l'elemento
  • initialInterval - l'intervallo iniziale da utilizzare per l'aggiornamento automatico (predefinito è di 30 secondi)

Possiamo anche vedere che usa un paio di chiavi; queste sono utilizzate per lo storage locale per ricordare lo stato dell'aggiornamento automatico. Potete vedere che uso il actionurl come parte della chiave per rendere questo endpoint specifico.

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() Metodo

Questo metodo consente o disabilita il sondaggio automatico di un elemento HTML di destinazione utilizzando HTMX.

Comportamento

  • Seleziona un elemento dal suo endpointId.
  • Costruisce l'URL di richiesta (fullUrl) combinando il dato actionUrl con la stringa di interrogazione della pagina corrente.
  • Controlla se il sondaggio è stato precedentemente abilitato leggendo da localStorage (buono come è ricordato tra le sessioni del browser).

Quando this.autoUpdate è true (cioè, il sondaggio è abilitato):

  • Imposta gli attributi HTMX sull'elemento:
    • hx-trigger per interrogare ogni interval secondi
    • hx-swap="innerHTML" sostituire il contenuto degli elementi [50]
    • hx-get per indicare l'URL dei sondaggi
    • hx-headers per aggiungere una personalizzazione "AutoPoll": "auto" intestazione
  • Salva lo stato abilitato a localStorage
  • Chiamate htmx.process(el) per permettere a HTMX di riconoscere i nuovi attributi
  • Se è stato precedentemente spento, attiva immediatamente una richiesta HTMX tramite htmx.ajax() (non affidandosi al cablaggio di eventi HTMX)

Quando this.autoUpdate è false (cioè, i sondaggi sono disabilitati):

  • Rimuove gli attributi HTMX di cui sopra
  • Pulisce l'impostazione salvata da localStorage
  • Chiamate htmx.process(el) di nuovo per aggiornare il comportamento HTMX

Sondaggio automatico quando abilitato per la prima volta

Abbiamo anche un ramo qui per eseguire l'auto-poll quando è abilitato per la prima volta.

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

Questo esegue una richiesta HTMX al fullUrl e aggiorna l'elemento bersaglio con la risposta. Questo è utile per mostrare all'utente come sarà l'aggiornamento automatico quando lo abilitano.

Intestazioni

Noterete che vi invieremo anche un'intestazione HTMX con la richiesta. Questo è importante in quanto ci permette di rilevare il lato del server di richiesta.

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

Nel mio server rilevo quindi che questa intestazione viene impostata usando

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

Vedrete che cerco solo l'intestazione con Request.Headers.TryGetValue("AutoPoll", out _) E se e' li', so che e' una richiesta di auto-poll.

Poi prendo lo yime corrente (è per un cliente francese, quindi mi converto all'ora di Parigi) e faccio un brindisi con l'ora.

Mostratorno

La ShowToast metodo è un semplice metodo di estensione che imposta un trigger per dire a HTMX di mostrare un messaggio di brindisi.

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

    }

Questo viene poi rilevato dal mio componente toast HTMX che mostra il messaggio.

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

Questo poi chiama nel mio componente Toast I scritto su qui .

Tiralo su.

E 'abbastanza semplice per collegare questo modulo, nel tuo main.js \ index.js qualsiasi cosa solo importarlo e collegarlo a Window

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

Poi chiamiamo il metodo init nel codice Razor ASP.NET:

ASP.NET Razor Code

Per rendere questo il più piccolo e riutilizzabile possibile il codice Razor è abbastanza semplice.

Qui potete vedere che ho specificato gli attributi dati Alpine.js per impostare l'aggiornamento automatico.

  • x-data: Qui è dove abbiamo impostato l'oggetto dati Alpine.js.
  • x-init: È qui che chiamiamo il metodo init sul controller dell'aggiornamento automatico.
  • x-on:change: È qui che chiamiamo il metodo toggleAutoUpdate sul controller auto-update.
  • data-endpoint-id: questo è l'id dell'elemento da aggiornare.
  • data-action-url: Questo è l'url da chiamare per aggiornare l'elemento.
  • intervallo dati: questo è l'intervallo da usare per l'aggiornamento automatico (il valore predefinito è 30 secondi).

Vedrete che abbiamo impostato l'obiettivo da utilizzare per la richiesta al campaignemail-request-list elemento. Questo è l'elemento che verrà aggiornato con i nuovi contenuti. Questo viene poi incluso SOMEWHERE nella pagina.

Ora, quando una casella di controllo è selezionata, aggiornerà automaticamente la lista ogni 30 secondi.

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

In conclusione

E questo è tutto, abbastanza semplice giusto. Levaging HTMX e Alpine.js per creare un semplice componente di aggiornamento automatico che possiamo utilizzare facilmente da ASP.NET Core.

logo

©2024 Scott Galloway