Harry Payne
Harry Payne

Reputation: 63

How to have a toggle all checkbox in svelte

I'm trying to set up a list of checkboxes and a button which allows toggling all the buttons, my code below:

<script>
  const items = ["a", "b", "c"]
  let selected = [];

  function toggleAll() {
    if (selected.length === 0) {
      selected = [...items]
    } else {
      selected = [];
    }
  }
</script>

<div>
    {selected.length} Selected
    <div>
        <input 
            type="checkbox" 
            on:click|preventDefault={toggleAll} 
            checked={selected.length === items.length}
        />
        <strong>All</strong>
    </div>
    {#each items as item}
        <div>
            <input type="checkbox" bind:group={selected} name={item} value={item}  />
            {item}
        </div>
    {/each}
</div>

This has some weird behaviour, if I click the all checkbox all the items are checked but not the all checkbox, but if I click each item without pressing the all checkbox it then is checked.

I think the preventDefault is doing something, because if I remove that the all works as expected, but then checking 1 item, then checking the all checkbox leaves the all checked and none of the items.

Upvotes: 3

Views: 1787

Answers (2)

Corrl
Corrl

Reputation: 7721

I think the preventDefault has no effect but it's the logic inside toggleAll - selecting all is the priority?
If the 'master' checkbox is clicked when not all other checkboxes are checked -> select all.
If all are checked -> deselect all.
REPL

<script>
    const items = ["a", "b", "c"]
    let selected = [];
    $: allSelected = items.length === selected.length

    function toggleAll() {
        if (allSelected) {
            selected = [];
        } else {
            selected = [...items]
        }
    }
</script>

<div>
    {selected.length} Selected
    <div>
        <input 
                     type="checkbox" 
                     on:click={toggleAll} 
                     checked={allSelected}
                     />
        <strong>All</strong>
    </div>
    {#each items as item}
    <div>
        <input type="checkbox" bind:group={selected} name={item} value={item}  />
        {item}
    </div>
    {/each}
</div>

Upvotes: 2

brunnerh
brunnerh

Reputation: 185225

I would use the change event and the current DOM checked state to toggle:

<input 
    type="checkbox" 
    on:change={toggleAll} 
    checked={selected.length === items.length} />
function toggleAll(e) {
    selected = e.target.checked ? [...items] : [];
}

REPL

Btw, you should use label elements to label form inputs.

Upvotes: 3

Related Questions