Svelte checkboxes suddenly can’t be checked

Below is the code to reproduce my problem (Svelte 3.0.0).

Repro steps:

  1. Click a checkbox. (behaves normally)
  2. Type some characters into the search box.
  3. Delete the characters you just typed.
  4. Click a checkbox again. Note that now it has no effect. The checkbox stays blank.

EDIT: It seems to be related to this line:

bind:group={selectedVars[schemaVar.name].lts}

If I hardcode that to:

bind:group={selectedVars['bar'].lts}

Then it works fine.

Any ideas why the filter breaks the checkboxes?

<script>
  let schema = {
    name: "bar",
    vars: [{ name: "X" }],
  };

  $: availableLTs = ["aaa", "bbb", "ccc", "abc"];

  function matchesSearch(searchString, text) {
    let query = searchString;
    console.log("query is", query);
    if (!query) return true;
    return text.includes(query);
  }

  let selectedChart = 'bar';
  let selectedVars = {X: { lts: [] }};
  $: schemaVars = schema.vars;
  let searchString = "";
</script>

<main>
  {#each schemaVars as schemaVar}
    <h6>
      {schemaVar.name}
      <input type="text" bind:value={searchString} placeholder="Search..." />
    </h6>
    {#each availableLTs as ele}
      {#if matchesSearch(searchString, ele)}
        <div class="form-check">
          <label>
            <input
              type="checkbox"
              class="form-check-input"
              bind:group={selectedVars[schemaVar.name].lts}
              value={ele}
            />
            {ele}
          </label>
        </div>
      {/if}
    {/each}
  {/each}
</main>

Answer

Looks like the problem has something to do with the nested each loops

  {#each schemaVars as schemaVar}
    ...
    {#each availableLTs as ele}
      
    ...//Checkbox

    {/each}
  {/each}

which can be solved through wrapping the inner loop inside a component.

The previous answer almost solved the problem, but one important thing is missing: The each loop inside the component is not a “keyed each block” ->

  1. select the third checkbox ‘ccc’
  2. filter for ‘c’
  3. -> ‘ccc’ moves to the first position and is not checked anymore

(alternatively select ‘aaa’ and filter for ‘b’…)

This can be solved by adding the label text as key/id (assuming that the checkbox labels are unique) ->

{#each filteredLTs as ele (ele)}        
        ...
{/each}

Since the problem wasn’t the way the variable was set (…selectedVars[schemaVar.name].lts}), I would try to keep the logic inside the component general and set the variables specifially inside the component tag inside App.svelte -> the component stays universal and can be easyly reused Example in this REPL

[App.svelte]
<script>
import Checkboxes from './Checkboxes.svelte'
    
let schema = {
   name: "bar",
   vars: [{ name: "X" }],
};

let allLTs = ["aaa", "bbb", "ccc", "abc"];
let searchString = "";
    
$: filteredLTs = allLTs.filter(lt => lt.includes(searchString)) 
let selectedVars = {X: { lts: [] }};
    
$: console.log(selectedVars.X.lts)
    
</script>

<main>
  {#each schema.vars as schemaVar}
    <h6>
      {schemaVar.name}
      <input type="text" bind:value={searchString} placeholder="Search..." />
    </h6>
    
    <div class="form-check">
    <Checkboxes checkboxes={filteredLTs} bind:checked={selectedVars[schemaVar.name].lts}/>
  </div>
  {/each}
</main>
[Checkboxes.svelte]
<script>
    export let checkboxes, checked  
</script>

{#each checkboxes as checkbox (checkbox)}        
        <label>
            <input
              type="checkbox"
              class="form-check-input"
              bind:group={checked}
              value={checkbox}
            />
            {checkbox}
   </label>
{/each}