Search code examples
vue.jsvuejs3v-forv-model

When I use v-model on input element that has a parent with v-for, it only let's me put 1 character at a time


I'm working on a Todo List app with Vue. But I've encountered an issue when trying to implement the editing of the todo's names. This is my todo list template:

<div v-for="(todo, index) in todos" :key="todo.title" class="todo-item">
  <div>
    <!-- <div v-if="!todo.editing" class="item-label"
      @dblclick="editTodo(todo)" >{{ todo.title }}</div> -->
    <input
      @dblclick="todo.editing = true"
      @blur="todo.editing = false"
      @keydown.enter="todo.editing = false"
      class="item-edit"
      type="text" :readonly="!todo.editing ? true : null"
      v-model="todo.title">
  </div>
  <div class="remove-item" @click="removeTodo(index)">
    &times; 
  </div>
</div>

I originally had a div containing the todo's title. But now i've replaced it with a input, that's readonly but when doubled clicked becomes a normal input. But, when the input is editable, I can only input 1 character before it deselects. I believe the reason for why is because every time I input a character the todo list updates and because of that it deselects. How could I fix this?


Solution

  • I modified your code a bit and hope the following solution helps you. I used two conditions to determine whether the title should be displayed or the input box. You can run the code snippet here and test if it serves your purpose. Let me know if you have any confusions.

    new Vue({
          el: '#app',
          data: {
            todos: [
                        {
                            title: 'title- 1',
                            editing: true
                        },
                        {
                            title: 'title- 2',
                            editing: true
                        },
                        {
                            title: 'title- 3',
                            editing: false
                        }
                    ],
                    showInput: false,
                    updateTitle: '',
                    selectedIndex: -1
          },
          methods: {
            editTodo(index){
                    if(this.todos[index].editing){
                        this.selectedIndex = index;
                        this.updateTitle = this.todos[index].title;
                        this.showInput = true;
                    }
                },
                updateTodo(index){
                    this.todos[index].title = this.updateTitle;
                    this.showInput = false;
                    this.selectedIndex = -1;
                },
                removeTodo(index){
                    this.todos.splice(index, 1);
                }
          }
        })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <div id="app">
            <div v-for="(todo, index) in todos" :key="todo.title" class="todo-item">
                <div v-if="index != selectedIndex" class="item-label" @dblclick="editTodo(index)" >
                    {{ todo.title }}
                    <span class="remove-item ml-2" @click="removeTodo(index)" style="cursor: pointer;"> &times; </span>
                </div>
    
                <input
                    v-if="showInput && (index == selectedIndex)"
                    class="item-edit"
                    type="text"
                    v-model="updateTitle"
                    @blur="updateTodo(index)"
                    v-on:keyup.enter="updateTodo(index)"
                >
            </div> 
        </div>