NOTE: Apart from
(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
//Less than a minute
Dans un post précédent, je vous ai montré comment créer un recherche déroulante en utilisant Alpine.js et HTMX puis j'ai montré comment nous pourrions activer la protection Cross-Site Request Forgery en utilisant le AntiforgeryRequestToken
dans ASP.NET Core avec JavaScript utilisant HTMX pour implémenter un cache Donut HoleC'est ce que j'ai dit. L'une des questions en suspens était de savoir comment il a chargé des pages.
Le problème était que j'utilisais HTMX AJAX pour effectuer le chargement de la page demandée une fois que vous aviez sélectionné le résultat de la page déroulante. Ce seul KINDA a fonctionné.
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 affichée avec la nouvelle, il a gâché le bouton arrière. Comme la page n'était pas vraiment chargée dans l'histoire correctement.
Comme avec mon dernier article sur le bouton arrière shennanigans C'était quelque chose que je voulais réparer.
Comme précédemment, la solution consistait à laisser HTMX s'en occuper directement. Pour ce faire, j'ai mis à jour mon modèle que j'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 génère maintenant approprié Les liens HTMX dans ce bloc de code. Laissez-nous utiliser le comportement HTMX correct.
typeahead.js
Pour l'activer dans mon code JavaScript, j'ai ajouté ce qui suit à ma méthode de recherche (voir ci-dessous). Les this.$nextTick
est une construction Alpine.js qui retarde cela jusqu'à ce qu'Alpine ait terminé le traitement du modèle que j'ai montré ci-dessus.
J'utilise alors htmx.process()
sur l'élément de recherche qui assurera le fonctionnement des attributs HTMX comme prévu.
.then(data => {
this.results = data;
this.highlightedIndex = -1; // Reset index on new search
this.$nextTick(() => {
htmx.process(document.getElementById('searchresults'));
});
})
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");
});
}
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
});
}
Ceci est sélectionné par le biais du clic 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 va ensuite charger la page et mettre à jour l'URL correctement.
J'ai aussi du code dans la boîte parent whick vous permet d'utiliser les touches fléchées et entrer.
<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 verrez que cela a tout le code nécessaire pour vous permettre de simplement cliquer sur Entrée et naviguer sur la page sélectionnée.
Juste un article de mise à jour rapide à la page déroulante de recherche existante pour améliorer l'expérience utilisateur lors de l'utilisation de la recherche. Encore une fois, il s'agit d'un utilisateur MINIMAL face au changement, mais il améliore simplement l'expérience utilisateur ; qui en tant que développeur web est votre principale préoccupation (au-delà d'être payé :)).