Back to "En smartare sökning Dropdown med HTMX"

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 HTMX

En smartare sökning Dropdown med HTMX

Monday, 16 September 2024

Inledning

I ett tidigare inlägg visade jag dig hur man skapar en sök dropdown med Alpine.js och HTMX Sedan visade jag hur vi kunde möjliggöra Cross-Site Request Forgery skydd med hjälp av AntiforgeryRequestToken i ASP.NET Core med JavaScript med hjälp av HTMX för att implementera en Donut Hole-cache....................................... En av de viktigaste frågorna var hur den laddade sidor.

Problemet

Problemet var att jag använde HTMX AJAX för att ladda den begärda sidan när du hade valt resultatet från rullgardinssidan. Det här fungerade bara KINDA.

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

Problemet var att medan detta skulle ladda rätt sida och uppdatera den visade webbadressen med den nya, det förstörde upp den bakre knappen. Eftersom sidan inte var riktigt laddad i historien korrekt.

Som med min förra artikeln på baksidan knapp shennanigans Det var något jag ville fixa.

Lösningen

Liksom tidigare var lösningen att låta HTMX hantera detta direkt. För att göra detta har jag uppdaterat min mall som jag använder för sökresultat.

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

Du kommer att se att jag nu genererar korrekt HTMX länkar i det här kodblocket. Låt oss använda rätt HTMX-beteende.

typeahead.js

För att aktivera detta i mitt gränssnitt JavaScript-kod lade jag till följande i min sökmetod (visas nedan). I detta sammanhang är det viktigt att se till att this.$nextTick är en Alpine.js konstruktion som försenar detta tills Alpine har avslutat behandlingen av mallen jag visade ovan.

Jag använder sedan htmx.process() på sökelementet som kommer att se till att HTMX-attributen fungerar som förväntat.


.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");
                });
        }
Senare när en sida är vald hanterar jag koden för att välja sidan, klicka på länken en rensa resultaten (för att stänga sökrutan).
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
            });
        }

Detta väljs genom att klicka på länken i sökresultaten.

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

Som sedan kommer att ladda sidan och uppdatera webbadressen korrekt.

Jag har också kod i överliggande rutan whoick kan du använda piltangenterna och ange.

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

Du kommer att se att detta har alla de koder som krävs för att du bara ska kunna trycka på enter och navigera till den valda sidan.

Slutsatser

Bara en snabb uppdatering artikel till den befintliga sök dropdown för att förbättra användarupplevelsen när du använder sökning. Återigen detta är en MINIMAL användare inför förändring men bara förbättrar användarupplevelsen, som som en webbutvecklare är din främsta oro (bortom att få betalt :)).

logo

©2024 Scott Galloway