Back to "HTMX (і невеличкі альпійські.js) для SPA-подібного досвіду в ядрах ASP. NET"

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 (і невеличкі альпійські.js) для SPA-подібного досвіду в ядрах ASP. NET

Sunday, 15 September 2024

Вступ

На цьому сайті я використовую HTMX Широко, це дуже простий спосіб змусити ваш сайт почуватися більш чуйним і плавнішим, без необхідності писати багато JavaScript.

ЗАУВАЖЕННЯ: я ще не зовсім задоволений цим, у HTMX є деякі елементи, які роблять цю роботу заплутаною

Ядро HTMX і ASP.NET

Використання HTMX з ядром ASP.NET є трохи мрією, я написав про це раніше в цей допис. Це дуже легко для Вашого сайту, і це чудовий спосіб, щоб Ваш сайт почувався більш реагуючим.

Звичайні посилання

Як частина моєї останньої поїздки, використовуючи HTMX, я вирішив зробити якомога більше посилань на цьому сайті, використовуючи HTMX якомога більше. Наприклад, якщо ви натиснете кнопку " Домівка," розташовану над нею, тепер використовуєте HTMX для завантаження вмісту сторінки.

Я досягну цього, просто додавши деякі властивості до теґу посилання, ось так:

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

Тут ви бачите, що я додаю hx-swap attribute, за допомогою цього параметра можна наказати HTMX поміняти вміст елемента призначення на вміст, повернений з сервера.

ЗАУВАЖЕННЯ: НЕ використовуйте hx- pushurl, оскільки це змусить навігатора двічі перезавантажити сторінку на задній кнопці.

The hx-target attribute повідомляє HTMX, куди пересунути вміст повернений з сервера. В цьому випадку це #contentcontainer element (де завантажено весь вміст сторінки).

Я також додаю hx-swap="show:window:top За допомогою цього пункту можна перегорнути вікно на вершину сторінки, якщо вміст буде змінено.

Частина C# є досить простою, вона просто дублює мій звичайний метод індексування, але натомість завантажує частковий перегляд. Нижче показано код. Знову це використовується HTMX. NET для виявлення hx-request заголовок і повернути частковий перегляд. Є також скорочення, щоб повернути звичайний вигляд, якщо запит не є запитом на HTMX.

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

Результатом цього є те, що це посилання (та інші використовують ту саму методику) навантаження на запити " AJAX " і вміст змінено без перезавантаження сторінки на повну. Це уникає набридливого спалаху світла, який ви отримаєте, коли сторінка перезавантажується через те, як працює зміна теми.

Посилання блогу (простий спосіб)

Таким чином, метод, поданий нижче, може бути приведений до роботи, але це дуже складно і трохи незграбно. HTMX дуже любить працювати за допомогою методу, заснованого на атрибутах. Користування htmx.ajax є свого роду "якщо все інше зазнає невдачі" метод і означає, що вам потрібно переплутати історію Борсера. На жаль, журнал переглядача використовує pushState replaceState і popstate це дійсно трохи біль в шиї (але може бути дуже потужним для випадків ребра).

Замість цього, я просто додаю hx-boost до мітки, що містить вміст блогу.

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

Це, здається, добре працює і hx-boost можна використовувати батьківські методи і застосовувати до всіх зв'язків між дітьми. Це також більш стандартний HTMX, не втручаючись в історію і т.д.

У рідкісних випадках, коли ви НЕ хочете використовувати HTMX для окремих посилань, ви можете просто додати hx-boost="false" з посиланням.

Посилання блогу (NOTE: я відмовився від цього підходу, але зберіг його тут для посилання)

Але проблема з цим підходом була в тому, що мій блог посилається на такі посилання: HTMX для SPA-подібного досвіду які не є " нормальними " посиланнями. Ці посилання створюються інструментом обробки markdown. Під час написання додатка MarkDig для додавання атрибутів HTMX до посилань, автор вирішив скористатися іншим методом (оскільки у мене вже є тонна вмісту, я не хочу перепаровувати).

Замість цього я додав функцію JavaScript, яка шукає всі ці типи посилань, а потім використовує htmx.ajax зробити заміну. Це, по суті, те, що HTMX робить в будь-якому випадку просто "вручну."

Як ви бачите, це просто функція, яка шукає всі посилання div.prose елемент, який починається з a / а тоді додає слухачеві події. Якщо посилання було натиснуто, події буде заборонено, буде видобуто адресу URL, а потім посилання htmx.ajax function викликається за допомогою адреси URL і елемента призначення для оновлення.

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

Крім того, після того як помінялися місцями, я переводжу нову адресу до історії браузера і прокручую сторінки на вершину сторінки. Я використовую цей підхід в декількох місцях (такий як пошук) для того, щоб дати масла гладким відчуттям.

Кнопка зворотного зв' язку (Це важко працювати)

Зворотна кнопка у цьому типі програми може бути проблематичною, багато з них " властиві " SCAS або вимкнути кнопку зворотного зв' язку, або виконати таку неправильну поведінку. Проте я хотів переконатися, що задня кнопка працює так, як очікувалося. Примітка: Щоб зробити так, як очікувалося, нам також потрібно послухати popState Замечательно. Цю подію буде запущено під час переходу користувача назад або вперед у історії переглядача. @ info: whatsthis Після запуску цієї події ми можемо перезавантажити вміст поточної адреси URL.

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

У цьому коді ми запобігаємо типовій поведінці навігатора за допомогою event.preventDefault() Дзвінок. Далі ми витягуємо поточний URL з window.location.href властивість і виконання запиту HTMX AJAX на завантаження вмісту поточного стану. Як тільки зміст буде завантажено, ми гортатимемо його на вершину сторінки.

Як вже згадувалося раніше, ви не можете користуватися ТАКОЖ hx-pushurl оскільки за допомогою цього пункту можна наказати переглядачу повторно перезавантажити сторінку на бічній кнопці.

Загалом, HTMX (і альпійські.js)

Як ви, можливо, здогадалися, я дещо фанат HTMX, разом з ядром ASP.NET і розбиття альпійських.js це дає змогу devs створити дійсно чудовий досвід користувача з мінімальними зусиллями. Основна річ, яку я віддаю перевагу таким, як "докладніше сформовані" клієнтські оболонки на кшталт React / Кутовий тощо... це те, що вона все ще надає мені повний канал відображення на сервері з ядром ASP. NET, але "відчуття" SPA. Альпійський.js надає можливість простої взаємодії з клієнтами, дуже проста функція, яку я нещодавно додав, робить мої категорії "прихованими," але розгорненими після клацання.

Використання альпійських.js для цього означає, що додаткового 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>

Тут ви бачите, що я використовую x-data атрибут створення нового об' єкта даних альпійської. js, у нашому випадку showCategories. Це булівське значення, яке буде перемикано, якщо h4 clicked element. The x-on:click атрибут використовується для прив' язки події клацання до функції- перемикача.

В межах списку категорій Я потім використовую x-show атрибут, який слід показати або сховати список категорій на основі значення showCategories. Я також використовую x-transition attribute, щоб додати ефект спадання під час показу або приховування категорій. The x-cloak attribute використовується для запобігання запуску JavaScript для показу категорій. У мене є маленький клас CSS визначений для цього:

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

Включення

Ось і все, коротка стаття про те, як використовувати HTMX і місце альпійського.js, щоб ваш сайт почувався більш чуйним і "SPA-подібним." Я сподіваюсь, що вам це стане у пригоді, якщо у вас є запитання або коментарі, будь ласка, залиште їх внизу.

logo

©2024 Scott Galloway