Een slimmere zoekafbraak met HTMX (Nederlands (Dutch))

Een slimmere zoekafbraak met 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.

Monday, 16 September 2024

//

5 minute read

Inleiding

In een vorige post liet ik je zien hoe je een zoekdropdown met Alpine.js en HTMX toen liet ik zien hoe we Cross-Site Request Forgery bescherming met behulp van de AntiforgeryRequestToken in ASP.NET Core met JavaScript HTMX gebruiken om een Donut Hole cache te implementeren. Een openstaande kwestie was hoe het pagina's laadde.

Het probleem

Het probleem was dat ik HTMX AJAX gebruikte om de gevraagde pagina te laden zodra u het resultaat van de drop-down pagina had geselecteerd. Dit heeft alleen KINDA gewerkt.

  selectResult(result) {
            htmx.ajax('get', result.url, {
                target: '#contentcontainer',  // The container to update
                swap: 'innerHTML',            // Replace the content inside the target
            }).then(function() {
                history.pushState(null, '', result.url);
                window.scrollTo({
                    top: 0,
                    behavior: 'smooth'
                });
            });

Het probleem was dat terwijl dit de juiste pagina zou laden en het bijwerken van de getoonde URL met de nieuwe, het verknoeide de achterknop. Omdat de pagina niet echt in de geschiedenis werd geladen.

Zoals met mijn laatste artikel op de achterknop shennanigans Dit was iets wat ik wilde oplossen.

De oplossing

Zoals bij eerder was de oplossing om HTMX dit direct te laten afhandelen. Om dit te doen heb ik mijn sjabloon bijgewerkt die ik gebruik voor zoekresultaten.

_typeahead.cshtml

<div x-data="window.mostlylucid.typeahead()" class="relative" id="searchelement" x-on:click.outside="results = []">
    @Html.AntiForgeryToken()
    <label class="input input-sm bg-neutral-500 bg-opacity-10 input-bordered flex items-center gap-2">
        <input
            type="text"
            x-model="query"
            x-on:input.debounce.200ms="search"
            x-on:keydown.down.prevent="moveDown"
            x-on:keydown.up.prevent="moveUp"
            x-on:keydown.enter.prevent="selectHighlighted"
            placeholder="Search..."
            class="border-0 grow input-sm text-black dark:text-white bg-transparent w-full"/>
        <i class="bx bx-search"></i>
    </label>
    <!-- Dropdown -->
    <ul x-show="results.length > 0"
        id="searchresults"
        class="absolute z-100 my-2 w-full bg-white dark:bg-custom-dark-bg border border-1 text-black dark:text-white border-neutral-600 rounded-lg shadow-lg">
        <template x-for="(result, index) in results" :key="result.slug">
            <li :class="{
                'dark:bg-blue-dark bg-blue-light': index === highlightedIndex,
                'dark:hover:bg-blue-dark hover:bg-blue-light': true
            }"
                class="cursor-pointer text-sm p-2 m-2">
                <!-- These are the key changes.-->
                <a
                    x-on:click="selectResult(index)"
                    @* :href="result.url" *@
                    :hx-get="result.url"
                    hx-target="#contentcontainer"
                    hx-swap="innerHTML"
                    hx-push-url="true"
                    x-text="result.title"
                   >
                </a>
                <-- End of changes -->
            </li>
        </template>
    </ul>

</div>

Je zult zien dat ik nu genereer. correct HTMX links in dit codeblok. Laat ons het juiste HTMX gedrag gebruiken.

typeahead.js

Om dit in mijn backend JavaScript code aan te zetten heb ik het volgende toegevoegd aan mijn zoekmethode (hieronder weergegeven). De this.$nextTick is een Alpine.js constructie die dit vertraagt totdat Alpine klaar is met het verwerken van het hierboven getoonde sjabloon.

Ik gebruik dan htmx.process() op het zoekelement dat ervoor zorgt dat de HTMX-attributen werken zoals verwacht.


.then(data => {
 this.results = data;
this.highlightedIndex = -1; // Reset index on new search
 this.$nextTick(() => {
    htmx.process(document.getElementById('searchresults'));
 });
})
typeahead.js search
   search() {
            if (this.query.length < 2) {
                this.results = [];
                this.highlightedIndex = -1;
                return;
            }
            let token = document.querySelector('#searchelement input[name="__RequestVerificationToken"]').value;
            console.log(token);
            fetch(`/api/search/${encodeURIComponent(this.query)}`, { // Fixed the backtick and closing bracket
                method: 'GET', // or 'POST' depending on your needs
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': token // Attach the AntiForgery token in the headers
                }
            })
                .then(response => {
                    if(response.ok){
                        return  response.json();
                    }
                    return Promise.reject(response);
                })
                .then(data => {
                    this.results = data;
                    this.highlightedIndex = -1; // Reset index on new search
                    this.$nextTick(() => {
                        htmx.process(document.getElementById('searchresults'));
                    });
                })
                .catch((response) => {
                    console.log(response.status, response.statusText);
                    if(response.status === 400)
                    {
                        console.log('Bad request, reloading page to try to fix it.');
                        window.location.reload();
                    }
                    response.json().then((json) => {
                        console.log(json);
                    })
                    console.log("Error fetching search results");
                });
        }
Later als een pagina is geselecteerd, selecteer ik de code om de pagina te selecteren, klik op de link om de resultaten te wissen (om het zoekvak te sluiten).
selectHighlighted() {
            if (this.highlightedIndex >= 0 && this.highlightedIndex < this.results.length) {
                this.selectResult(this.highlightedIndex);
                
            }
        },

        selectResult(selectedIndex) {
       let links = document.querySelectorAll('#searchresults a');
       links[selectedIndex].click();
            this.$nextTick(() => {
                this.results = []; // Clear the results
                this.highlightedIndex = -1; // Reset the highlighted index
                this.query = ''; // Clear the query
            });
        }

Dit wordt geselecteerd via de onclick van de link in de zoekresultaten.

 <a
  x-on:click="selectResult(index)"
  :hx-get="result.url"
  hx-target="#contentcontainer"
  hx-swap="innerHTML"
  hx-push-url="true"
  x-text="result.title"
  >
  </a>

Die vervolgens de pagina zal laden en de URL correct bijwerken.

Ik heb ook code in de ouder doos whick kunt u de pijltjestoetsen gebruiken en invoeren.

    <label class="input input-sm bg-neutral-500 bg-opacity-10 input-bordered flex items-center gap-2">
        <input
            type="text"
            x-model="query"
            x-on:input.debounce.200ms="search"
            x-on:keydown.down.prevent="moveDown"
            x-on:keydown.up.prevent="moveUp"
            x-on:keydown.enter.prevent="selectHighlighted"
            placeholder="Search..."
            class="border-0 grow input-sm text-black dark:text-white bg-transparent w-full"/>
        <i class="bx bx-search"></i>
    </label>

U zult zien dat dit alle code heeft die nodig is om u in staat te stellen om gewoon te klikken op enter en navigeren naar de geselecteerde pagina.

Conclusie

Gewoon een snelle update artikel naar de bestaande zoekdropdown om de gebruikerservaring te verbeteren bij het gebruik van zoeken. Opnieuw is dit een MINIMALE gebruiker geconfronteerd met verandering, maar alleen verbetert de gebruikerservaring; die als een webontwikkelaar zijn uw primaire zorg (voorbij het krijgen van betaald :)).

logo

©2024 Scott Galloway