Search code examples
javascriptvue.jsvue-composition-api

How do I clear the form fields after submission in this Vue 3 app?


I am working a small book-list app with Vue 3 and the composition API.

In App.vue I have:

<script setup>
    import { ref } from 'vue'
    import Book from './Book.vue'
    import AddBook from './AddBook.vue'

    const books = ref([])

    const addBook = function(newBook) {
      books.value.push(newBook);
    }
</script>

<template>
  <div class="container-fluid">
    <h1 class="display-4">Reading list for 2024</h1>
    <ol v-if="books.length" class="books">
      <Book v-for="(book, index) in books" :book="{
        title: book.title,
        author: book.author
      }" />
    </ol>
    <p class="text-center" v-else>No books to display</p>

     <AddBook @add-book="addBook" />
  </div>
</template>

In Book.vue I have:

<script setup>
 defineProps({
  book: Object
})
</script>

<template>
  <li>
    <i>{{ book.title }}</i> by  {{ book.author }} 
    <span class="delete" @click="$emit('delete-book', index)">&#x2716</span>
  </li>
</template>

The form for adding a book is in AddBook.vue:

<script setup>
  import { ref, defineEmits } from 'vue'
  const emit = defineEmits(['add-book'])

  const isValid = ref(true);

  const newBook = {
    title: '',
    author: ''
  }

  const handleSubmit = function(){
    isValid.value = false;
    if(newBook.title.length && newBook.author.length){
     isValid.value = true;
      emit('add-book', newBook);
    }
  }
</script>

<template>
<h2 class="display-4">Add a book</h2>
 <form @submit.prevent="handleSubmit">
  <div v-if="!isValid" class="p-2 text-center alert alert-danger">
    The form data is invalid!
  </div>
  <div class="mb-2">
    <label for="title" class="form-label">Title</label>
    <input id="title" class="form-control form-control-sm" type="text" v-model="newBook.title">
  </div>
    
  <div class="mb-2">
    <label for="author" class="form-label">Author</label>
    <input id="author" class="form-control form-control-sm" type="text" v-model="newBook.author">
  </div>
  <div class="mb-2">
    <button class="btn btn-sm btn-success w-100" type="submit">Add</button>  
  </div>
 </form>
</template>

The goal

After adding a book, I want the form fields to be cleared.

The problem

Doing this in order to achieve the goal does clear the fields, but it also empties the title and author properties of the newBook object:

emit('add-book', newBook);
// Clear fields
newBook.title = '',
newBook.author = ''

Questions

  1. What am I doing wrong?
  2. What is the most reliable way to fix this problem?

Solution

  • The problem is that newBook is cleared before the parent component has a change to access it.

    You could create a copy of the newBook object before emitting:

    <script setup>
      import { ref, defineEmits } from 'vue'
      const emit = defineEmits(['add-book']);
    
      const isValid = ref(true);
    
      const newBook = {
        title: '',
        author: ''
      }
    
      const handleSubmit = function(){
        isValid.value = false;
        if(newBook.title.length && newBook.author.length){
          isValid.value = true;
    
          // Create a copy of newBook before emitting
          const bookCopy = { ...newBook };
          emit('addBook', bookCopy);
          
          // Clear the original newBook object
          newBook.author = '';
          newBook.title = '';
        }
      }
    </script>