NOTE: Apart from
(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
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.
Quindi volevo che questo fosse...
Con questo in mente ho deciso di costruire un piccolo modulo JS utilizzando Alpine.js e HTMX.
È 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 per questo è davvero piuttosto compatto, è suddiviso in due parti principali: un modulo JS, i gestori di eventi e l'HTML.
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 aggiornareactionUrl
- l'url da chiamare per aggiornare l'elementoinitialInterval
- 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()
MetodoQuesto metodo consente o disabilita il sondaggio automatico di un elemento HTML di destinazione utilizzando HTMX.
endpointId
.fullUrl
) combinando il dato actionUrl
con la stringa di interrogazione della pagina corrente.localStorage
(buono come è ricordato tra le sessioni del browser).this.autoUpdate
è true
(cioè, il sondaggio è abilitato):hx-trigger
per interrogare ogni interval
secondihx-swap="innerHTML"
sostituire il contenuto degli elementi [50]hx-get
per indicare l'URL dei sondaggihx-headers
per aggiungere una personalizzazione "AutoPoll": "auto"
intestazionelocalStorage
htmx.process(el)
per permettere a HTMX di riconoscere i nuovi attributihtmx.ajax()
(non affidandosi al cablaggio di eventi HTMX)this.autoUpdate
è false
(cioè, i sondaggi sono disabilitati):localStorage
htmx.process(el)
di nuovo per aggiornare il comportamento HTMXAbbiamo 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.
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.
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 .
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:
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.
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>
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.