Une décharge de recherche plus intelligente avec HTMX (Français (French))

Une décharge de recherche plus intelligente avec HTMX

Monday, 16 September 2024

//

5 minute read

Introduction

Dans un précédent post je vous ai montré comment créer une descendre de recherche en utilisant Alpine.js et HTMX puis j'ai montré comment nous pouvions assurer la protection contre la fausse demande de site Cross- en utilisant le AntiforgeryRequestToken dans ASP.NET Core avec JavaScript Utiliser HTMX pour mettre en œuvre un cache Donut Hole. Une question en suspens était la façon dont les pages étaient chargées.

Le problème

Le problème était que je faisais appel à HTMX AJAX pour charger la page demandée une fois que vous avez sélectionné le résultat de la page descendante.

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

Le problème était que, même si cela allait charger la page droite et mettre à jour l'URL montrée avec une nouvelle , il a défectué le bouton arrière. Comme la page WASNM SK2T REALLY loaded into the history properlyMSC3

Comme dans mon cas dernier article sur les shennanigans du bouton arrière c’est quelque chose que je voulais corriger.

La solution

Comme auparavant, la solution consistait à laisser HTMX traiter ce problème directement. Pour cela j’ai mis à jour mon modèle que je utilise pour les résultats de recherche.

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

Vous verrez que je vais maintenant générer intégralité Liens HTMX dans ce bloc de code. Ce qui nous permet d'utiliser le comportement correct HT MX.

typeahead.js

Pour ce faire dans mon code JavaScript de backend, j’ai ajouté ce qui suit à ma méthode de recherche ( montré ci-dessous). this.$nextTick est une construction Alpine.js qui retarde ce processus jusqu’à ce que l’Alpine ait terminé le traitement du modèle que j’ai montré ci-dessus

Je puis utiliser htmx.process() sur l'élément de recherche qui garantira que les attributs HTMX fonctionnent comme prévu.


.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");
                });
        }
Plus tard, une fois qu'une page est sélectionnée, je manipule le code pour sélectionner la page, cliquez sur le lien et je clarifie les résultats M SK1pour fermer la case de recherche ).
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
            });
        }

Cette option est sélectionnée en cliquant sur le lien dans les résultats de recherche.

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

Ce qui chargera ensuite la page et mettra à jour correctement l’URL.

J'ai également un code dans la case parentale qui vous permet d'utiliser les touches à flèche et de saisir.

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

Vous ' constaterez que ce fichier contient tous les codes nécessaires pour vous permettre de cliquer sur l’entrée et de naviguer vers la page sélectionnée.

En conclusion

Just a quick update article to the existing search dropdown to enhance the user experience when using search. Again this is a MINIMAL user facing change but just enhances the User Experience; who as a web developer are your primary concern M SK2beyond getting paid :)).

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.