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
In un post precedente ti ho mostrato come creare un ricerca a discesa utilizzando Alpine.js e HTMX poi ho mostrato come abbiamo potuto abilitare Cross-Site Richiesta di protezione falso utilizzando il AntiforgeryRequestToken
in ASP.NET Core con JavaScript utilizzando HTMX per implementare una cache Donut Hole. Un problema in sospeso era come ha caricato le pagine.
Il problema era che stavo usando HTMX AJAX per effettuare il caricamento della pagina richiesta una volta selezionato il risultato dalla pagina a discesa. Questo solo KINDA ha funzionato.
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'
});
});
Il problema era che mentre questo avrebbe caricato la pagina giusta e aggiornare l'URL visualizzato con il nuovo, ha incasinato il pulsante posteriore. Dato che la pagina non e' stata davvero caricata correttamente nella cronologia.
Come con la mia ultimo articolo sul pulsante posteriore shennanigans Era una cosa che volevo sistemare.
Come in precedenza la soluzione era quella di lasciare che HDMX gestisse direttamente questo. Per fare questo ho aggiornato il mio modello che uso per i risultati di ricerca.
_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>
Vedrai che ora sono io a generare corretta Collegamenti HTMX in questo blocco di codice. Permettendoci di utilizzare il corretto comportamento HTMX.
typeahead.js
Per abilitare questo nel mio codice JavaScript di backend ho aggiunto il seguente al mio metodo di ricerca (mostrato qui sotto). La this.$nextTick
è una costruzione Alpine.js che ritarda questo fino a quando Alpine ha finito di elaborare il modello che ho mostrato sopra.
Poi uso htmx.process()
sull'elemento di ricerca che garantirà che gli attributi HTMX funzionino come previsto.
.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
});
}
Questo viene selezionato tramite il clic del link nei risultati della ricerca.
<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>
Che poi caricherà la pagina e aggiornerà correttamente l'URL.
Ho anche il codice nella casella genitore whick permette di utilizzare i tasti freccia e entrare.
<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>
Vedrete che questo ha tutto il codice necessario per permettervi di premere Invio e navigare nella pagina selezionata.
Solo un articolo di aggiornamento rapido per la ricerca esistente a discesa per migliorare l'esperienza dell'utente quando si utilizza la ricerca. Ancora una volta questo è un utente MINIMAL che affronta il cambiamento, ma solo migliora l'esperienza dell'utente; che come sviluppatore web sono la vostra preoccupazione principale (oltre a essere pagato:)).