Home > Mobile >  svelte forms de-structuring binding
svelte forms de-structuring binding

Time:10-29

My code is working, but I just wonder if there's a tidier way of achieving what I've done with some de-structuring. Essentially I've started of from the code here:

https://svelte.dev/repl/2441993f8d9946aa894bf07a8a8f9b4f?version=3.52.0

and nest the array within an object and added a delete button like so:

<script>
        let f={
        "name" : "", "type" : "",
        "values":[{ "label": "", "start": "", "end": "" },
                            { "label": "", "start": "", "end": "" },
                            { "label": "", "start": "", "end": "" }]
        }; 

        // let { name, type, values } = f
        
        const addField = () => {f.values = [...f.values, {label: '', start: '', end: ''}]}; 
        const removeField = (v, i) => {f.values = f.values.filter((e, id) => id!=i)}
    </script>

    <input type="text" bind:value={f.name} placeholder="name"/>
    <input type="text" bind:value={f.type} placeholder="type"/>

    {#each f.values as v, i}
    <div>
        <input id={i} type="text" bind:value={f.values[i].start} placeholder="start"/>
        <input id={i} type="text" bind:value={f.values[i].end} placeholder="end"/>
        <input id={i} type="text" bind:value={f.values[i].label} placeholder="label"/>
        <input id={i} type="button" value="X" on:click={removeField(v, i)}>
    </div>
    {/each}

    <button on:click|preventDefault={addField}>Add</button>

    <button on:click={() => console.log(f.values)}>Save</button>

    <pre>
     {JSON.stringify(f, null, 2)}
    </pre>

As far as I've tested it it appears to work, but I just wonder if there's easier ways to bind nested values of the object, so the code looks a little cleaner rather than referencing 'f.' everywhere.

When I've tried referencing the de-structured values the binding gets lost - I suppose this is normal behaviour.

CodePudding user response:

In the #each the v variable will be the current item which you can use directly:

{#each values as v, i}
    <div>
        <input type="text" bind:value={v.start} placeholder="start"/>
        <input type="text" bind:value={v.end} placeholder="end"/>
        <input type="text" bind:value={v.label} placeholder="label"/>
        <input type="button" value="X" on:click={removeField(v, i)}>
    </div>
{/each}

For the script part, there would be certain ugly workarounds with re-assignments or dummy assignments, but the cleanest solution would probably be to extract everything about the values into a separate component, e.g.

<!-- Values.svelte -->
<script>
    export let values;

    const addField = () => {values = [...values, {label: '', start: '', end: ''}]};
    const removeField = item => {values = values.filter(x => x != item)}
</script>

{#each values as v, i}
    <div>
        <input type="text" bind:value={v.start} placeholder="start"/>
        <input type="text" bind:value={v.end} placeholder="end"/>
        <input type="text" bind:value={v.label} placeholder="label"/>
        <input type="button" value="X" on:click={removeField(v)}>
    </div>
{/each}

<button on:click|preventDefault={addField}>Add</button>

Which then can be added as:

<Values bind:values={f.values} />

REPL

(By the way, id attributes are not allowed to start with numbers and have to be unique in the entire page.)

  • Related