Search code examples
javascriptvue.jsbootstrap-modalbootstrap-5

Vue send data to modal


I have 3 components: TaskList, TaskItem and TaskForm.

The TaskList contains a loop to render all data in TaskItem. Each TaskItem have a edit buttom, that i want to open a TaskForm (modal using bootstrap) with the task data.

TaskForm.vue

  <div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" id="formModal">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" id="staticBackdropLabel">{{ props.task.name }}</h1>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
      </div>
    </div>
  </div>

TaskList.vue

  <div class="container">
    <TaskItem
      v-for="task in props.tasks"
      :key="task.uuid"
      :task="task"
    />
  </div>

TaskItem.vue

  <div class="row align-items-center p-2 border rounded mt-2">
    <div class="col-3">
    {{ props.task.name }}
    </div>
      <button class="btn btn-primary" @click="showForm = !showForm" data-bs-toggle="modal" data-bs-target="#formModal">
        <i class="bi bi-pencil-square"></i>
      </button>
  </div>

  <TaskForm v-if="showForm" :task="props.task"/>

But when i do it like this, in every modal, i get the first task name, instead of the selected one.


I don't think this is the best way to do what I want to do in the end (edit a list item), but it's the one I came up with. If someone has a better proposal, I will be happy to read it.


Solution

  • I would recommend using the TaskForm component only once, this can be achieved using props and emits like this

    // for parent component
    <template>
    <ChildComponent v-for="item in items" key={item.id} :item="item" @edit-item="showEditModal" />
    <PopUpComponent v-if="selectedItem !== null" :selected-item="selectedItem" @close="selectedItem = null" />
    </template>
    <script setup>
    import {ref} from "vue";
    const selectedItem = ref(null);
    function showEditModal(item) {
    selectedItem.value = item;
    }
    
    </script>
    

    For child component we need to define an emit named edit-item that will call this function

    // for child
    <template>
    ..other data
    <button @click="editItem">edit</button>
    </template>
    <script setup>
    const emit = defineEmits(["edit-item"]);
    
    function editItem() {
    emit('edit-item', props.item) // this will call the @edit-item in parent and that function will set a selectedItem non-null value which will render the modal
    }
    </script>
    

    Similarly for a close function in modal

    <template>
    ...
    <button @click="closeModal">close</button>
    </template>
    <script setup>
    const emit = defineEmits(["close"])
    function closeModal() {
    emit("close")
    }
    </script>