Home > front end >  Why is the edit button not working? Svelte
Why is the edit button not working? Svelte

Time:07-31

In the below code, why is the edit button for the invidual todos not working? I have it bound to the function editTodo which sets the isEdited object property to true.

It works when I declare the function inline, e.g: <button on:click='{() => isEdited = true}>, but not by seperating the function (<button on:click='{() => editTodo(isEdited)}>).

Could anyone explain why that is and adjust the below code so that editing works with the separate function?

The code:

<script>
    let todos = [
        {
            desc: 'Get some groceries for cooking',
            isDone: false,
            isEdited: false,
        },
        {
            desc: 'Wash the dishes and clean the house',
            isDone: false,
            isEdited: false,
        },
    ];
    
    let newTodo;
    function createTodo() {
        todos = [...todos, {desc: newTodo, isDone: false, isEdited: false}];
        newTodo = null;
    }
    
    function editTodo(isEdited) {
        isEdited = true;
    }
    
    function deleteTodo(desc) {
        todos = todos.filter(todo => todo.desc !== desc);
    }
    
    $: console.log(todos);
</script>

<h1>
    Todoapp
</h1>
<form on:submit|preventDefault='{createTodo}'>
    <label for='create-todo' class='create-todo-label'>
        Create a new todo:
    </label>
    <input bind:value='{newTodo}' type='text' placeholder='Do something' id='create-todo' required />
    <button>
        Create
    </button>
</form>

{#if todos.length !== 0}
    <h2>
        Your todos:
    </h2>
{/if}

<ul>
    {#each todos as {desc, isDone, isEdited}, index}
        <li>
            <label for='{index}' class:done='{isDone}'>
                {desc}
            </label>
            <input id='{index}' bind:checked='{isDone}' type='checkbox' />
        </li>
    
        <button on:click={() => editTodo(isEdited)}>
            Edit
        </button>
        {#if isEdited}
            <textarea cols='30' rows='5' bind:value='{desc}' />
        {/if}
    
        <button on:click='{() => deleteTodo(desc)}'>
            Delete
        </button>
    {/each}
</ul>

<style>
    .create-todo-label {
        margin-block-end: 0.25rem;
    }
    
    label {
        max-inline-size: fit-content;
    }
    
    label.done {
        text-decoration: line-through;
    }
</style>

CodePudding user response:

Iam not an svelte expert , but while looking into your code, you're just sending the parameter and changing the parameter to true, which wont reflect in actual todos array , I tried the below method and it worked

<button on:click={() => editTodo(index)}>
  Edit
</button>
function editTodo(index) {      
  todos[index].isEdited = true;
}

CodePudding user response:

Svelte does something here which will not work in regular JS, namely destructuring an object and then modifying the destructured properties would not affect the source object. So the fact that bind:checked='{isDone}' works is more unexpected than editTodo not working.

Declaring a function like editTodo creates a new lexical scope in which the argument is decoupled from what it originally referred to. In plain JS, assigning a new value to a function argument will simply do nothing to the original source of the argument.

The same appears to be true here. By invoking a function, Svelte's magic association between the value and its source breaks.

You can use an ID or the index to find the correct element in the array and update that. Or instead of destructuring the items you can pass the item and replace it in the array with an updated version (using object identity to find it, instead of ID or index).

  • Related