Back to "HTMX (ja vähän Alpine.js) ASP.NET Coren SPA:n kaltaiseen kokemukseen"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Alpine.js ASP.NET HTMX

HTMX (ja vähän Alpine.js) ASP.NET Coren SPA:n kaltaiseen kokemukseen

Sunday, 15 September 2024

Johdanto

Tässä kohdassa käytän HTMX Laaja-alaisesti tämä on superhelppo tapa saada sivustosi tuntemaan olosi vastaanottavaisemmaksi ja sujuvammaksi ilman, että sinun tarvitsee kirjoittaa paljon JavaScriptia.

HUOMAUTUS: En ole täysin tyytyväinen tähän vielä, HTMX:ssä on elementtejä, jotka tekevät tästä hankalaa

HTMX ja ASP.NET Core

HTMX:n käyttö ASP.NET Coren kanssa on aikamoinen unelma, olen kirjoittanut tästä aiemmin tämä postaus...................................................................................................................................... Sivustosi "HTMX:ää" on todella helppo "tehdä", ja se on hyvä tapa saada sivustosi tuntemaan olosi vastaanottavaisemmaksi.

Normaalit linkit

Osana viimeisintä kierrostani HTMX:n avulla päätin tehdä mahdollisimman monta linkkiä tällä sivustolla käyttää HTMX:ää. Joten esimerkiksi jos klikkaat "Koti"-painiketta, se käyttää nyt HTMX:ää sivun sisällön lataamiseen.

Saavutan tämän yksinkertaisesti lisäämällä joitakin ominaisuuksia linkin tagiin, kuten näin:

    <a asp-action="IndexPartial" asp-controller="Home" hx-swap="show:window:top"  hx-target="#contentcontainer" hx-boost="true" class="mr-auto sm:mr-6">
     <div class="svg-container">
        <img src="/img/logo.svg" asp-append-version="true"  width="180px" height="30px"  alt="logo"  :class="{ 'img-filter-dark': isDarkMode }"/>
     </div>
    </a>

Tässä näette, että lisään... hx-swap attribuutti kertoo HTMX:n vaihtavan kohde-elementin sisällön palvelimelta palautetun sisällön kanssa.

HUOMAUTUS: ÄLÄ käytä hx-pushurlia, koska tämä saa selaimen lataamaan sivun takanappiin kahdesti.

Erytropoietiini hx-target Attribuutti kertoo HTMX:lle, mihin palvelimelta palautettu sisältö laitetaan. Tässä tapauksessa se on #contentcontainer elementti (jossa kaikki sivun sisältö on ladattu).

Lisään myös: hx-swap="show:window:top joka tekee ikkunan kääröstä sivun yläosan, kun sisältöä vaihdetaan.

C#-osa on melko yksinkertainen, se vain kopioi normaalin Index-menetelmäni, mutta lataa sen sijaan osittaisen näkymän. Koodi on alla. Jälleen tämä käyttää HTMX.NET Havaitsemaan hx-request Header and return the partial view. Normaalin näkymän palauttamiseen on myös mahdollisuus, jos pyyntö ei ole HTMX-pyyntö.

    [Route("/IndexPartial")]
    [OutputCache(Duration = 3600, VaryByHeaderNames = new[] { "hx-request" })]
    [ResponseCache(Duration = 300, VaryByHeader = "hx-request",
        Location = ResponseCacheLocation.Any)]
    [HttpGet]
    public async Task<IActionResult> IndexPartial()
    {
        ViewBag.Title = "mostlylucid";
        if(!Request.IsHtmx()) return RedirectToAction("Index");
        var authenticateResult = await GetUserInfo();
        var posts = await BlogService.GetPagedPosts(1, 5);
        posts.LinkUrl = Url.Action("Index", "Home");
     
        var indexPageViewModel = new IndexPageViewModel
        {
            Posts = posts, Authenticated = authenticateResult.LoggedIn, Name = authenticateResult.Name,
            AvatarUrl = authenticateResult.AvatarUrl
        };
        return PartialView("_HomePartial", indexPageViewModel);
    }

Tuloksena on, että tämä linkki (ja muut samaa tekniikkaa käyttävät) lataa AJAX-pyyntöjä ja sisältö vaihdetaan ilman koko sivun uudelleenlatausta. Näin vältät ärsyttävän "valon välähdyksen", kun sivu latautuu uudelleen, koska teemani vaihto toimii.

Blogilinkit (yksinkertaisempi tapa)

Joten alla oleva menetelmä voidaan saada toimimaan, mutta se on erittäin hankala ja hieman kiusallinen. HTMX todella haluaa työskennellä attribuuttipohjaisella lähestymistavalla. Käyttäminen htmx.ajax on tavallaan "jos kaikki muu epäonnistuu" -menetelmä ja tarkoittaa, että pitää sotkea borwserin historiaa. Valitettavasti selainhistoriaa käytetään pushState replaceState sekä popstate on todella vähän niskakipua (mutta voi olla supervoimakas reunatapauksissa).

Sen sijaan se, mitä nyt teen, vain lisään hx-boost blogin sisältöä sisältävään lapulle.

   <div class="prose prose max-w-none border-b py-2 text-black dark:prose-dark sm:py-2" hx-boost="true"  hx-target="#contentcontainer">
        @Html.Raw(Model.HtmlContent)
    </div>

Tämä näyttää toimivan hyvin. hx-boost Voidaan soveltaa vanhempien menetelmiin ja soveltaa kaikkiin lapsilinkkeihin. Se on myös tavallisempi HTMX sotkematta historiaa jne.

Harvoissa tilanteissa, joissa et halua käyttää HTMX:ää tietyissä linkeissä, voit vain lisätä hx-boost="false" linkkiin.

Ongelmana tässä lähestymistavassa oli kuitenkin tämänkaltainen blogilinkkini: HTMX SPA:n kaltaiseen kokemukseen jotka eivät ole "normaaleja" yhteyksiä. Nämä linkit syntyy markdown-palkinnosta; vaikka voisin kirjoittaa MarkDig-laajennuksen lisätäkseni HTMX-attribuutit lenkkeihin, päätin käyttää erilaista lähestymistapaa (koska minulla on jo paljon sisältöä, jota en halua muokata).

Sen sijaan lisäsin Javascript-toiminnon, joka etsii kaikkia tämäntyyppisiä linkkejä ja sitten käyttää htmx.ajax vaihdoksen tekemiseen. Tämä on pohjimmiltaan se, mitä HTMX tekee joka tapauksessa vain "manuaalisesti".

Kuten näet, tämä on vain yksinkertainen toiminto, joka etsii kaikki linkit div.prose elementti, joka alkaa / ja lisää sitten niihin tapahtuman kuuntelijan. Kun linkkiä napsautetaan, tapahtuma estetään, URL poistetaan ja sen jälkeen htmx.ajax Funktio kutsutaan URL-osoitteella ja päivitettävällä kohde-elementillä.

(function(window) {
   
    window.blogswitcher =() =>  {
        const blogLinks = document.querySelectorAll('div.prose  a[href^="/"]');

        // Iterate through all the selected blog links
        blogLinks.forEach(link => {
            link.addEventListener('click', function(event) {
               event.preventDefault();
                let link = event.currentTarget;
                let url = link.href;
                htmx.ajax('get', url, {
                    target: '#contentcontainer',  // The container to update
                    swap: 'innerHTML',            // Replace the content inside the target


                }).then(function() {
                    history.pushState(null, '', url);
                  
                        window.scrollTo({
                            top: 0,
                            behavior: 'smooth' // For a smooth scrolling effect
                        });
                    
                });
            });
        });
        
    };
})(window);

Kun vaihto on tehty, työnnän uuden URL-osoitteen selaimen historiaan ja selaa sivun yläreunaan. Käytän tätä lähestymistapaa muutamaan paikkaan (kuten hakuun) antaakseni mukavan voimaisen pehmeän kokemuksen.

Back Button (Tämä on hankalaa saada töihin)

Tämän tyyppisen sovelluksen takapainike voi olla ongelmallinen; monet "sopivat" SPA:t joko poistavat takapainikkeen käytöstä tai sietävät tätä väärää käytöstä. Halusin kuitenkin varmistaa, että takanappi toimii odotetusti. Huomautus: Jotta takanappi toimisi odotetusti, meidän on myös kuunneltava popState tapahtuma. Tapahtuma käynnistyy, kun käyttäjä navigoi takaisin tai eteenpäin selainhistoriassa. Kun tämä tapahtuma käynnistyy, voimme ladata sisällön nykyiseen URL-osoitteeseen.

window.addEventListener("popstate",   (event) => {
 // When the user navigates back, reload the content for the current URL
 event.preventDefault();
 let url = window.location.href;
 // Perform the HTMX AJAX request to load the content for the current state
 htmx.ajax('get', url, {
  target: '#contentcontainer',
  swap: 'innerHTML'
 }).then(function () {
  // Scroll to the top of the page
  window.scrollTo({
   top: 0,
   behavior: 'smooth'
  });
 });
});

Tässä koodissa estämme oletusselaimen popstate-käyttäytymisen event.preventDefault() puhelu. Sen jälkeen poistamme nykyisen URL-osoitteen window.location.href Omistus ja suorita HTMX AJAX -pyyntö sisällön lataamisesta nykytilaa varten. Kun sisältö on ladattu, selataan sivun yläosaan.

Kuten edellä mainittiin, et voi myös käyttää hx-pushurl Koska tämä saa selaimen lataamaan sivun backpainikella kahdesti.

HTMX yleisemmin (ja alppi.js)

Kuten ehkä arvasit, olen jonkin verran HTMX:n fani, yhdistettynä ASP.NET Coreen ja Alppien murskaamiseen.Js:n avulla Devs pystyy luomaan todella mukavan käyttökokemuksen mahdollisimman pienellä vaivalla. Tärkein asia, josta pidän enemmän kuin "täysin muodostuneiden" asiakaspuolen kehysten, kuten React / Angular jne., kaltaisista asioista, on se, että se antaa minulle yhä täyden palvelimenpuolisen renderointiputken ASP.NET Corella, mutta SPA:n "tunteen". Alpine.js mahdollistaa yksinkertaisen asiakasvuorovaikutteisuuden, joka on hyvin yksinkertainen ominaisuus, jonka lisäsin äskettäin, oli tehdä kategorioistani "piilotettuja", mutta laajenna klikkauksella.

Alpine.js:n käyttö tähän tarkoittaa, että ylimääräistä JavaScriptia ei tarvita.

        <div class="mb-8 mt-6 border-neutral-400 dark:border-neutral-600 border rounded-lg" x-data="{ showCategories: false }">
            <h4 
                class="px-5 py-1 bg-neutral-500  bg-opacity-10 rounded-lg  font-body text-primary dark:text-white w-full flex justify-between items-center cursor-pointer"
                x-on:click="showCategories = !showCategories"
            >
                Categories
                <span>
                    <i
                        class="bx text-2xl"
                        :class="showCategories ? 'bx-chevron-up' : 'bx-chevron-down'"
                    ></i>
                </span>
            </h4>
            <div 
                class="flex flex-wrap gap-2 pt-2 pl-5 pr-5 pb-2"
                x-show="showCategories" 
                x-cloak
                x-transition:enter="max-h-0 opacity-0"
                x-transition:enter-end="max-h-screen opacity-100"
                x-transition:leave="max-h-screen opacity-100"
                x-transition:leave-end="max-h-0 opacity-0"
            >
                @foreach (var category in ViewBag.Categories)
                {
                    <partial name="_Category" model="category"/>
                }
           
            </div>
        </div>

Tässä näet, että käytän x-data attribuutti uuden Alpine.js-tietoobjektin luomiseen, tässä tapauksessa showCategories...................................................................................................................................... Tämä on boolean, joka kietoutuu yhteen, kun h4 elementtiä klikataan. Erytropoietiini x-on:click Attribuuttia käytetään kytkemään naksahdustapahtuma toiminnolle.

Luokkien luettelossa I sitten käyttää x-show Attribuutti, jolla näytetään tai piilotetaan luettelo kategorioista, joiden arvo perustuu showCategories...................................................................................................................................... Käytän myös x-transition Attribuutti, joka lisää mukavan liukuvan vaikutuksen, kun kategoriat näytetään tai piilotetaan. Erytropoietiini x-cloak Ominaisuus käytetään estämään kategorioiden "flicker" näyttäminen ennen JavaScriptin käyttöä. Minulla on pieni CSS-kurssi, joka on määritelty tätä varten:

[x-cloak] { display: none !important; }

Johtopäätöksenä

Siinä se, lyhyt artikkeli siitä, miten HTMX:ää käytetään, sekä kohta Alppien.j:iä, jotta sivustosi tuntisi itsensä reagoivammaksi ja "SPA-maiseksi". Toivon, että pidit tätä hyödyllisenä, jos sinulla on kysyttävää tai kommentteja, voit jättää ne alle.

logo

©2024 Scott Galloway