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.
Sunday, 15 September 2024
//Less than a minute
På denna webbplats använder jag HTMX Ordförande I stor utsträckning är detta ett super enkelt sätt att få din webbplats att känna sig mer lyhörd och smidigare utan att behöva skriva mycket JavaScript.
OBS: Jag är inte helt nöjd med detta ännu, HTMX har några element som gör detta svårt
Att använda HTMX med ASP.NET Core är lite av en dröm, jag har skrivit om detta tidigare i detta inlägg....................................... Det är verkligen ganska lätt att 'HTMXify' din webbplats, och det är ett bra sätt att få din webbplats att känna sig mer lyhörd.
Som en del av min senaste gå runt med hjälp av HTMX bestämde jag mig för att göra så många länkar på denna webbplats använder HTMX som möjligt. Så till exempel om du klickar på "Home" knappen ovanför det nu använder HTMX för att ladda sidans innehåll.
Jag uppnår detta genom att helt enkelt lägga till några egenskaper till länktaggen, så här:
<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>
Här kan du se Jag lägger till hx-swap
attribut, detta säger HTMX att byta innehållet i målelementet med det innehåll som returneras från servern.
OBS: Använd INTE hx-pushurl eftersom detta gör att webbläsaren laddar om sidan på backknappen två gånger.
I detta sammanhang är det viktigt att se till att hx-target
attributet talar om för HTMX var innehållet som returneras från servern ska placeras. I det här fallet är det #contentcontainer
element (där allt sidinnehåll är laddat).
Jag lägger också till hx-swap="show:window:top
vilket gör att fönstret rullar till toppen av sidan när innehållet byts ut.
C# delen av detta är ganska enkel, det bara duplicerar min normala Index metod men istället laddar partiella vy. Koden visas nedan. Återigen använder detta HTMX.NET Ordförande för att upptäcka hx-request
sidhuvud och returnera delvyn. Det finns också en cut-out för att returnera den normala vyn om begäran inte är en HTMX begäran.
[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);
}
Resultatet av detta är att denna länk (och andra använder samma teknik) belastning som "AJAX" förfrågningar och innehållet byts ut utan en hel sida omladdning. Detta undviker irriterande "ljusblixt" du får när en sida laddar om på grund av hur mitt tema byta fungerar.
Så metoden nedan KAN göras för att fungera men det är super knepigt och lite pinsamt.
HTMX gillar verkligen att arbeta med attributet baserat tillvägagångssätt. Användning htmx.ajax
är typ av "om allt annat misslyckas" metoden och innebär att du måste röra till Borwser historia.
Tyvärr webbläsarhistorik med hjälp av pushState
replaceState
och popstate
är verkligen lite av en smärta i nacken (men kan vara super kraftfull för kant fall).
Istället vad jag gör nu är bara lägga till hx-boost
till taggen som innehåller blogginnehåll.
<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>
Detta verkar fungera bra och hx-boost
kan placeras på förälder metoder och tillämpas på alla barn länkar. Det är också mer standard HTMX utan att jävlas med historia etc.
I det sällsynta tillfälle som du inte vill använda HTMX för särskilda länkar kan du bara lägga till hx-boost="false"
till länken.
Men ett problem med detta tillvägagångssätt var min blogg länkar som denna: HTMX för en SPA-liknande upplevelse som inte är "normala" länkar. Dessa länkar genereras av markdown-tolken; medan jag kunde skriva en MarkDig-tillägg för att lägga till HTMX-attribut till länkarna, bestämde jag mig för att använda ett annat tillvägagångssätt (eftersom jag redan har massor av innehåll jag inte vill om-parse).
Istället lade jag till en JavaScript-funktion som letar efter alla dessa typer av länkar, sedan använder htmx.ajax
För att göra bytet. Detta är i huvudsak vad HTMX gör ändå bara "manuell".
Som ni kan se är detta bara en enkel funktion som letar efter alla länkar i div.prose
element som börjar med en /
och sedan lägger en händelse lyssnare till dem. När länken klickas förhindras händelsen, URL extraheras och sedan htmx.ajax
Funktionen kallas med webbadressen och målelementet att uppdatera.
(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);
Dessutom när bytet har gjorts trycker jag sedan på den nya URL:en till webbläsarens historik och bläddrar till toppen av sidan. Jag använder detta tillvägagångssätt på några ställen (som sökning) för att ge en fin smörig smidig upplevelse.
Bakknappen i denna typ av app kan vara problematisk; MÅNGA "riktiga" SPAs antingen inaktivera bakknappen eller stå ut med detta felaktiga beteende. Men jag ville se till att den bakre knappen fungerade som förväntat.
Obs: För att få den bakre knappen att fungera som förväntat måste vi också lyssna på popState
händelse. Denna händelse avfyras när användaren navigerar bakåt eller framåt i webbläsarens historik. När händelsen avfyras kan vi ladda om innehållet för den aktuella webbadressen.
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'
});
});
});
I denna kod förhindrar vi standard webbläsare popstate beteende med event.preventDefault()
Jag synar. Vi extraherar sedan den aktuella webbadressen från window.location.href
egendom och utföra en HTMX AJAX begäran om att ladda innehållet för det aktuella tillståndet. När innehållet har laddats rullar vi till toppen av sidan.
Som tidigare nämnts kan du inte heller använda hx-pushurl
eftersom detta kommer att göra att webbläsaren laddar om sidan på backknappen två gånger.
Som du kanske har gissat är jag något en beundrare av HTMX, i kombination med ASP.NET Core och en fadder av Alpine.js det ger devs för att skapa en riktigt trevlig användarupplevelse med minimal ansträngning. Det viktigaste jag föredrar framför de mer "fullständigt formade" klientsidan ramar som React / Angular etc... är att det fortfarande ger mig en full server-side rendering pipeline med ASP.NET Core men "känner" av ett SPA. Alpine.js möjliggör enkel klient sida interaktivitet, en mycket enkel funktion som jag nyligen lagt till var att göra mina kategorier "dolda" men expandera på ett klick.
Att använda Alpine.js för detta innebär att det inte behövs något extra JavaScript.
<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>
Här kan du se att jag använder x-data
attribut för att skapa ett nytt dataobjekt Alpine.js, i detta fall showCategories
....................................... Detta är en boolean som är växlad när h4
elementet klickas. I detta sammanhang är det viktigt att se till att x-on:click
egenskapen används för att binda klickhändelsen till funktionen Växla.
I listan över kategorier använder jag sedan x-show
attribut för att visa eller dölja listan över kategorier baserat på värdet av showCategories
....................................... Jag använder också x-transition
attribut för att lägga till en fin bildnedslagseffekt när kategorierna visas eller döljs.
I detta sammanhang är det viktigt att se till att x-cloak
egenskap används för att förhindra att 'flicker' av kategorierna visas innan JavaScript har körts.
Jag har en liten CSS klass definieras för detta:
[x-cloak] { display: none !important; }
Så det är det, en kort artikel om hur man använder HTMX och en plats av Alpine.js för att få din webbplats att känna sig mer lyhörd och "SPA-liknande". Jag hoppas att du finner detta användbart, om du har några frågor eller kommentarer är du välkommen att lämna dem nedan.