javascriptsveltesvelte-3svelte-2

Svelte todo app bug: title property returns undefined


I am working on a small ToDo application in Svelte for learning purposes (Im new to Svelte).

In the view I have:

<div class="input-group p-2" id="editForm">
  <input type="text" class="form-control" id="old_todo" value={currentToDo.title} placeholder="Edit Todo">
  <div class="input-group-append">
    <button on:click="{addTodo}" class="btn btn-sm btn-success">Save</button>
  </div>
</div>

//More code

{#each todos as t, index}   
<tr>
  <td>{index + 1}</td>
  <td class="{t.completed == true ? 'completed' : ''}">{t.title}</td>
  <td class="text-center"><input type="checkbox" on:change={completeTodo(t.id)}></td>
  <td class="text-right">
    <div class="btn-group">
      <button on:click="{editTodo(t.id)}" class="btn btn-sm btn-primary">Edit</button>
      <button on:click="{deleteTodo(t.id)}" class="btn btn-sm btn-danger">Delete</button>
    </div>
  </td>
</tr>
{/each}

The part of the script I believe is relevant here:

<script>
    import {onMount} from "svelte";

    const apiURL = "https://jsonplaceholder.typicode.com/todos?_start=1&_limit=20";

    let todos = [];

    onMount(async function() {
        const response = await fetch(apiURL);
        todos = await response.json();
        todos.reverse();
    });

    var currentToDo = {};

    function editTodo(tid) {
        let itemIdx = todos.findIndex(x => x.id == tid);
        let currentToDo = todos[itemIdx];
    }

<script>

See the REPL here.

The problem is that the text field <input type="text" class="form-control" id="old_todo" value={currentToDo.title} placeholder="Edit Todo"> displays undefined instead of the current (do be edited) todo title.

Where is my mistake?


Solution

  • Well, currentToDo.title really is undefined...

    You can change to currentToDo.title || '' if you want to avoid this display:

    <input type="text" class="form-control" id="old_todo" value={currentToDo.title || ''} placeholder="Edit Todo">
    

    Also, to make you Edit button work...

    Change this:

    <button on:click="{editTodo(t.id)}" class="btn btn-sm btn-primary">Edit</button>
    

    to this:

    <button on:click="{() => editTodo(t.id)}" class="btn btn-sm btn-primary">Edit</button>
    

    on:click="{editTodo(t.id)} becomes on:click="{() => editTodo(t.id)}, otherwise your editTodo function is called once per render, but not when the button is actually clicked (in Svelte you pass a function reference, not a function body like normal HTML onclick for example).

    Also, in your editTodo function:

        function editTodo(tid){
            let itemIdx = todos.findIndex(x => x.id == tid);
            let currentToDo = todos[itemIdx];
        } 
    

    You need to remove this let to use the root scope currentToDo, instead of a locally scoped variable:

        function editTodo(tid){
            let itemIdx = todos.findIndex(x => x.id == tid);
            currentToDo = todos[itemIdx];
        } 
    

    Then you can click on an Edit button and the title will be displayed in the input field as expected!