このToDoリストをSvelteのインスタント検索と組み合わせる方法は?

Nov 26 2020

tldrバージョンの編集は、以下のコードで非常に近いと表示されているところまでスクロールダウンします。

問題

私は、完了すると次の品質を持つ基本的なToDoリストを持っています。

  1. ページが読み込まれると、フォームフィールドにフォーカスが表示されます。
  2. ユーザーが上/下矢印をクリックすると、CSSの背景色を変更してリスト内のアイテムを選択する必要があります。
  3. ユーザーがフォームに新しいアイテムを入力すると、そのアイテムがリストに表示されます。
  4. ユーザーがフォームフィールドに入力すると、「インスタント検索」がリストに適用され、検索に一致するアイテムのみが表示されます。他のすべての機能は引き続き機能するはずです。ユーザーが上/下矢印を使用してスクロールすると、ページに表示されているアイテムの数に関係なく機能するはずです。2つのアイテムのみが表示されている場合、ユーザーは上下の矢印を使用してこれら2つのアイテムとフォームフィールドのフォーカスを選択できるはずです。

4番を除くすべてのアイテムは正常に機能しているようです。次のコードは、項目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)
}
 -->

上記のコードとインスタント検索機能を組み合わせたいと思います。

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

私がこれまでに試したことはすべて、混乱を招き、リストを返さず、入力フィールドのフォーカスを外します(onBlur)。

console.logの下のコードは、ユーザーが検索したときに必要なものを提供する配列です。次のステップは、それをDOMに適用することです。私がこれまで試したすべてがうまくいきませんでした。

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>




<!-- 


 -->

編集

私は以下のコードに非常に近いです。

このバージョンでは、唯一の問題は、ユーザーが新しいToDoを送信すると、リストがDOMに表示されないことです。舞台裏で他のすべてが機能します。意味を確認するには、入力フィールドに項目を追加して送信し、フォームフィールドに文字aを入力して、戻るスペースボタンを押します。リストが再表示されます。

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>




<!-- 


 -->

回答

William Nov 26 2020 at 22:53

わかった。クライアント配列のコピーをリアクティブ配列に設定しました。うまくいけば、これは誰かを助けるでしょう。

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>




<!-- 


 -->
SamGomena Nov 27 2020 at 03:14

これは、ここの要点にあるコードに基づいた、あなたが持っているもののもう少し簡潔なバージョンです。ただし、これは実質的にソリューションと同じです(つまり、個別の「フィルタリングされた」リストを作成して表示します)。

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