1

What is the way in Svelte to get the Controlled Inputs behaviour (like in React).

I tried to prevent default checkbox behaviour on input click (prevents HTML checkbox to modify the checked prop).

<input 
  type=checkbox 
  bind:checked={done} 
  on:click|preventDefault={() => dispatch('change'}}
  />

But apparently, it does something else - and svelte bindings stoped working.

Tried without bind:checked => just checked={done} - no results as well.

At the end I figured to do one-way binding via if:

{#if done}
  <input type=checkbox checked readonly />
{:else}
  <input type=checkbox readonly />
{/if}

but that looks lame.

What is the proper way to implement Controlled Inputs in Svelte?

Full simplified example you could find here: https://svelte.dev/repl/ecc812d1be34464185739f02ca2421cd?version=3.19.2

ValeriiVasin
  • 8,628
  • 11
  • 58
  • 78
  • I found this post helpful as well: https://stackoverflow.com/questions/155291/can-html-checkboxes-be-set-to-readonly – 425nesp Jan 02 '23 at 20:02

3 Answers3

1

One way this can be done is by turning off the pointer events for the checkbox and register a click event on a wrapper element that handles this.

<style>
    input {
        pointer-events: none;
    }
</style>

<label on:click|preventDefault={handleClick} on:keydown|preventDefault={handleClick}>
    <span>[current: {done}]</span>
    <input type="checkbox" checked={done}>
</label>

(I added the keydown handler so you can still toggle the checkbox with the keyboard as well)

Stephane Vanraes
  • 14,343
  • 2
  • 23
  • 41
  • 1
    oh, TIL that readonly on `:checkbox` is a react invention :D its not standard per-spec https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly :( therefore prob that is a nice addition. Wondering what svelte devs think :) – ValeriiVasin Mar 06 '20 at 16:28
0

If you bind to the value with bind:checked={done}, you're explicitly asking that the internal value of the checkbox be reflected to the done variable, so it seems to be the opposite of what you're asking.

To me, preventDefault seems like the way to go if you want to prevent the default input behaviour:

<script>
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher()

    export let done;

    const handleClick = () => {
        dispatch('change');
    }

    const noop = () => {}
</script>

<div class="root" on:click={handleClick}>
    <label>


        <input type=checkbox checked={done} on:click|preventDefault={noop} />


        <slot></slot> [current: {done}]
    </label>
</div>

Updated REPL

Maybe a more terse, old school, alternative could be this (notice, onclick, not on:click):

<input type=checkbox checked={done} onclick="return false" />

In the context of Svelte, some might cringe at this and call it a code smell, though... But it feels OK to me.

rixo
  • 23,815
  • 4
  • 63
  • 68
0

That's how you can implement controlled input:

<script>
    import { createEventDispatcher } from 'svelte';
    
    const dispatch = createEventDispatcher();
    
    export let checked = false;
    
    function handleChange(e) {
        const detail = e.target.checked;
        const ok = detail !== checked && dispatch('change', detail, { cancelable: true });
        ok || (e.target.checked = checked);
    }
</script>

<input type="checkbox" {checked} on:change={handleChange} />

And use it:

<script>
    import Checkbox from './Checkbox.svelte';
    
    let checked = false;
    
    function handleChange(e) {
        console.log(e.detail);
        e.preventDefault();
    }
</script>

<Checkbox {checked} on:change={handleChange} />

REPL here

grind-t
  • 16
  • 2