En automatisk uppdatering av deluppdatering med Alpine.js och HTMX (Svenska (Swedish))

En automatisk uppdatering av deluppdatering med Alpine.js och 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

Inledning

Så i det som håller på att bli en serie, i ett arbetsprojekt ville jag lägga till förmågan till en partiell till automatisk uppdatering på en given tidsskala.

Så här gjorde jag med Alpine.js och HTMX.

Krav

Så jag ville att detta skulle vara

  1. återanvändbar, så den ska vara enkel och tillräckligt självförsörjande för att automatiskt uppdatera alla element.
  2. Den bör respektera befintliga Url-parametrar
  3. Det bör vara detekterbar serversida (i ASP.NET Core i detta fall)
  4. Om aktiverad ska den aktiveras för det effektmåttet Endast och detta bör kommas ihåg mellan förfrågningar.
  5. Det bör omedelbart göra uppdateringen när aktiverad (så användaren vet hur det ser ut)
  6. Den ska kunna stängas av av användaren
  7. Det ska vara enkelt att ta med på en sida.

Med detta i åtanke började jag bygga en liten JS-modul med Alpine.js och HTMX.

ANMÄRKNING

Du kan göra automatiska uppdateringar 'på och av' och'minns' funktioner ganska enkelt med HTMX ensam. Till exempel; använda HTMX-utlösare Du kan verkligen göra en hel del saker.


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

Tack till @KhalidAbuhakmeh För att du påpekade det här.

Denna kod används faktiskt hx-trigger för att ställa in den automatiska uppdateringen. Använder bara Alpine.js för att konfigurera HTMX- attributen.

Vad detta verkligen lägger till är användarinteraktion klientsidan, vilket är vad Alpine.js är bra på.

Koden

Koden för detta är verkligen ganska kompakt, den är uppdelad i två huvuddelar; en JS-modul, händelsehanterare och HTML.

Modulen

Modulen är en enkel JS modul som använder Alpine.js för att hantera tillståndet för auto-update. Den använder lokal lagring för att komma ihåg läget för automatisk uppdatering mellan förfrågningar.

Det accepterar paramerna :

  • endpointId - ID för det element som skall uppdateras
  • actionUrl - url att kallas för att uppdatera elementet
  • initialInterval - det initiala intervall som ska användas för automatisk uppdatering (standard är 30 sekunder)

Vi kan också se det använder ett par nycklar; dessa används för lokal lagring för att komma ihåg tillståndet för auto-update. Du kan se att jag använder actionurl som en del av nyckeln för att göra denna endpoint specifik.

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

Denna metod möjliggör eller inaktiverar automatisk röstning av ett mål HTML-element med hjälp av HTMX.

Beteende

  • Väljer ett element genom sin endpointId.
  • Bygger webbadressen för begäran (fullUrl) genom att kombinera actionUrl med aktuell sidas frågesträng.
  • Kontrollerar om röstning tidigare har aktiverats genom att läsa från localStorage (bra som det är ihågkommen mellan webbläsarsessioner).

När this.autoUpdate är true (dvs. röstningen är aktiverad):

  • Ställer in HTMX- attribut på elementet:
    • hx-trigger för att välja ut varje interval sekunder
    • hx-swap="innerHTML" för att ersätta elementets innehåll
    • hx-get för att peka på röstadressen
    • hx-headers för att lägga till en egen "AutoPoll": "auto" Huvud
  • Sparar aktiverat tillstånd till localStorage
  • Samtal htmx.process(el) för att låta HTMX känna igen de nya attributen
  • Om den tidigare var avstängd utlöser den omedelbart en HTMX-förfrågan via htmx.ajax() (inte beroende på HTMX händelse ledningar)

När this.autoUpdate är false (dvs. röstningen är inaktiverad):

  • Tar bort ovanstående HTMX- attribut
  • Rensar den sparade inställningen från localStorage
  • Samtal htmx.process(el) igen för att uppdatera HTMX-beteende

Automatisk mätning när den först aktiverades

Vi har också en gren här för att utföra auto-pollen när den först aktiverats.

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

Detta utför en HTMX- begäran till fullUrl och uppdaterar målelementet med svaret. Detta är användbart för att visa användaren hur den automatiska uppdateringen kommer att se ut när de aktiverar den.

Huvuden

Du noterar att vi också skickar ett HTMX-huvud med begäran. Detta är viktigt eftersom det gör det möjligt för oss att upptäcka begäran serversidan.

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

I min serversida upptäcker jag sedan att denna rubrik är inställd med hjälp av

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

Du ska få se att jag bara letar efter huvudet med Request.Headers.TryGetValue("AutoPoll", out _) Och om det är där jag vet att det är en auto-poll begäran.

Jag tar sedan den nuvarande yime (det är för en fransk kund, så jag konverterar till Paris tid) och visa en skål med tiden.

Visa toast

I detta sammanhang är det viktigt att se till att ShowToast metod är en enkel förlängning metod som sätter en utlösare för att säga HTMX att visa ett rostat meddelande.

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

    }

Detta upptäcks sedan av min HTMX rostat bröd komponent som visar meddelandet.

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

Detta kallar sedan in min Toast komponent I skrev om här .

Jag kopplar upp den.

Det är ganska enkelt att koppla upp den här modulen, i din main.js \ index.js vad som helst bara importera den och koppla upp den till Fönster

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

Vi kallar sedan init-metoden i ASP.NET Razor-koden:

ASP.NET Razorkod

För att göra detta så litet och återanvändbart som möjligt är Razor-koden ganska enkel.

Här kan du se att jag anger dataattributen alpin.js för att ställa in automatisk uppdatering.

  • x-data: Här ställer vi in dataobjektet Alpine.js.
  • x-init: Det är här vi kallar init-metoden på auto-update-regulatorn.
  • x-on:change: Det är här vi kallar växlaAutoUpdate metoden på auto-update controllern.
  • data-endpoint-id: Detta är id för elementet som ska uppdateras.
  • data-action-url: Detta är den webbadress som ska kallas för att uppdatera elementet.
  • data-interval: Detta är intervallet som ska användas för automatisk uppdatering (standard är 30 sekunder).

Du ser att vi ställer in målet att använda för begäran till campaignemail-request-list Förutsättningar. Detta är det element som kommer att uppdateras med det nya innehållet. Det är då inkluderat NÅGRA på sidan.

Nu när en kryssruta är markerad kommer den automatiskt att uppdatera listan var 30 sekunder.

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

Slutsatser

Och det är det, ganska enkelt rätt. Medel HTMX och Alpine.js för att skapa en enkel automatisk uppdateringskomponent som vi enkelt kan använda från ASP.NET Core.

logo

©2024 Scott Galloway