Comment marier cette liste de tâches avec la recherche instantanée dans Svelte?
EDIT pour la version tldr faites défiler jusqu'à l'endroit où il est dit que je suis extrêmement proche dans le code ci-dessous.
Problème
J'ai une liste de tâches de base qui, une fois terminée, aura ces qualités:
- Lorsque la page se charge, le champ de formulaire doit être au point.
- Lorsque l'utilisateur clique sur les flèches haut / bas, les éléments de la liste doivent être sélectionnés via un changement de couleur d'arrière-plan CSS.
- Lorsque l'utilisateur tape un nouvel élément dans le formulaire, il doit apparaître dans la liste pour l'utilisateur.
- Lorsque l'utilisateur tape dans le champ du formulaire, une "recherche instantanée" est appliquée à la liste et seuls les éléments qui correspondent à la recherche apparaissent. Toutes les autres fonctionnalités devraient toujours fonctionner. Lorsque l'utilisateur fait défiler à l'aide des flèches haut / bas, cela devrait toujours fonctionner quel que soit le nombre d'éléments affichés sur la page. Si seuls deux éléments sont affichés, l'utilisateur doit pouvoir utiliser les flèches haut et bas pour sélectionner ces deux éléments et le focus du champ de formulaire.
Tous les éléments sauf le numéro 4 semblent fonctionner correctement. Le code suivant illustre les éléments 1 à 3
https://svelte.dev/repl/214b9c033d1c489e991484d387c267c2?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clients.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clients.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clients.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clients as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
console.log(filteredCharacters)
}
-->
Je veux marier le code ci-dessus avec une fonction de recherche instantanée.
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
console.log(filteredCharacters)
}
Tout ce que j'ai essayé jusqu'à présent fait du désordre, ne renvoie pas la liste et déconcentre (onBlur) le champ de saisie.
Le code ci-dessous console.log est un tableau qui me donne ce que je veux lorsque l'utilisateur recherche. L'étape suivante consiste à l'appliquer au DOM. Tout ce que j'ai essayé jusqu'à présent n'a pas fonctionné.
https://svelte.dev/repl/ceca685a3a4c49b4b5ccd780a101fc63?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
console.log(filteredCharacters)
}
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clients.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clients.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clients.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} on:input={search} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clients as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
-->
ÉDITER
Je suis extrêmement proche du code ci-dessous.
Dans cette version, le seul problème est que lorsqu'un utilisateur soumet une nouvelle tâche, la liste n'apparaît pas dans le DOM. Tout le reste fonctionne dans les coulisses. Pour voir ce que je veux dire, ajoutez un élément dans le champ de saisie, soumettez-le, puis tapez la lettre a dans le champ de formulaire, puis appuyez sur le bouton d'espacement arrière. La liste réapparaîtra.
https://svelte.dev/repl/f8fb733401eb4e27b9b8e54c688d355a?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let clientsCopy = [...clients];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
clientsCopy = [...filteredCharacters]
}
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clientsCopy.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clientsCopy.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clientsCopy.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} on:input={search} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clientsCopy as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
-->
Réponses
J? ai compris. J'ai défini une copie du tableau des clients sur un tableau réactif. Espérons que cela aide quelqu'un.
https://svelte.dev/repl/ad1c185bf97a47b98506c3603f510de6?version=3.30.0
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
$: clientsCopy = clients ;
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
onMount(function() {
clientInputTextField.focus();
});
function search(e){
console.log(e.target.value)
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clients.filter((character) => {
return (
character.toLowerCase().includes(searchString) ||
character.toLowerCase().includes(searchString)
);
});
clientsCopy = [...filteredCharacters]
}
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clientsCopy.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clientsCopy.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clientsCopy.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} on:input={search} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each clientsCopy as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>
<!--
-->
Ceci est une version un peu plus succincte de ce que vous avez, basée sur le code trouvé dans l'essentiel ici . C'est effectivement la même chose que votre solution (c'est-à-dire créer et afficher une liste "filtrée" séparée)!
<script>
import { onMount } from 'svelte';
let clients = ["a Little piggy","b little piggy","c little piggy"];
let indexVal = -1;
let downArrowPress = 40;
let upArrowPress = 38;
let clientInputTextField = "";
let clientVal = "";
const startsWith = (item, query) => item.toLowerCase().indexOf(query.toLowerCase()) != -1;
$: filteredClients = clientVal && clientVal.length ? clients.filter(item => startsWith(item, clientVal)) : clients;
onMount(function() {
clientInputTextField.focus();
});
function handleKeydown(event) {
if(event.keyCode === upArrowPress) {
indexVal-=1;
indexVal = indexVal < 0 ? clients.length : indexVal
console.log(indexVal);
}
if(event.keyCode === downArrowPress) {
indexVal+=1;
indexVal = indexVal > clients.length ? 0 : indexVal
console.log(indexVal);
}
if(indexVal > clients.length -1 || indexVal < 0){
clientInputTextField.focus();
} else {
clientInputTextField.blur()
}
}
function onChange(e) {
console.log("what")
const searchTerm = e.target.value.toLowerCase();
console.log(searchTerm)
clients = clients.filter(client => client.toLowerCase().includes(searchTerm))
}
function handleSubmit(e) {
const value = e.target.input.value;
console.log(value);
clients = [...clients, value];
clientVal = "";
}
</script>
<svelte:window on:keydown={handleKeydown}/>
<form on:submit|preventDefault={handleSubmit}>
<!-- <input type="text" name="input" on:change={onChange} bind:this={clientInputTextField} bind:value={clientVal} autocomplete="off"> -->
<input type="text" name="input" bind:this={clientInputTextField} bind:value={clientVal} autocomplete="off">
<input type="submit" name="">
</form>
<ul>
{#each filteredClients as client, i}
{#if i === indexVal}
<li style="background-color:orange">{client}</li>
{:else}
<li style="">{client}</li>
{/if}
{/each}
</ul>