Back to "Ein intelligenter Such-Dropdown mit 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

HTMX Alpine.js

Ein intelligenter Such-Dropdown mit HTMX

Monday, 16 September 2024

Einleitung

In einem früheren Beitrag habe ich Ihnen gezeigt, wie man eine Suche Dropdown mit Alpine.js und HTMX dann habe ich gezeigt, wie wir Cross-Site Request Forgery Schutz mit dem AntiforgeryRequestToken im ASP.NET Core mit JavaScript mit HTMX einen Donut Hole Cache implementieren......................................................................................................... Ein herausragendes Problem war, wie es Seiten geladen.

Das Problem

Das Problem war, dass ich HTMX AJAX benutzte, um die angeforderte Seite zu laden, sobald Sie das Ergebnis von der Dropdown-Seite ausgewählt hatten. Das hat nur KINDA funktioniert.

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

Das Problem war, dass, während dies würde die richtige Seite laden und aktualisieren Sie die angezeigte URL mit der neuen, es verwüstet die Rückseite Taste. Wie die Seite war nicht wirklich in die Geschichte richtig geladen.

Wie bei meinem letzter Artikel auf der Rückseite Knopf Shennanigans Das war etwas, das ich reparieren wollte.

Die Lösung

Wie bisher war es die Lösung, HTMX dies direkt handhaben zu lassen. Dazu habe ich meine Vorlage aktualisiert, die ich für Suchergebnisse verwende.

_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 wirst sehen, dass ich jetzt generiere sachgerecht HTMX-Verbindungen in diesem Code-Block. Lassen Sie uns das richtige HTMX-Verhalten verwenden.

typeahead.js

Um dies in meinem Backend-JavaScript-Code zu aktivieren, habe ich die folgende Suchmethode hinzugefügt (siehe unten). Das this.$nextTick ist ein Alpine.js-Konstrukt, das dies verzögert, bis Alpine die oben gezeigte Vorlage bearbeitet hat.

Dann verwende ich htmx.process() auf dem Suchelement, das sicherstellt, dass die HTMX-Attribute wie erwartet funktionieren.


.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");
                });
        }
Später, sobald eine Seite ausgewählt ist, handle ich den Code, um die Seite auszuwählen, klicken Sie auf den Link und löschen Sie die Ergebnisse (um das Suchfeld zu schließen).
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
            });
        }

Diese wird über den Klick auf den Link in den Suchergebnissen ausgewählt.

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

Das wird dann die Seite laden und die URL richtig aktualisieren.

Ich habe auch Code in der Elternbox whick ermöglicht es Ihnen, die Pfeiltasten zu verwenden und eingeben.

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

Sie werden sehen, dass dies alle notwendigen Code hat, damit Sie einfach Enter drücken und zur ausgewählten Seite navigieren können.

Schlussfolgerung

Nur eine schnelle Aktualisierung Artikel auf die bestehende Suche Dropdown, um die Benutzererfahrung zu verbessern, wenn die Suche. Auch dies ist ein MINIMAL Benutzer gegenüber Änderung, sondern nur verbessert die Benutzererfahrung; wer als Web-Entwickler sind Ihre primäre Sorge (jenseits immer bezahlt :)).

logo

©2024 Scott Galloway