vue.jsvuejs3vue-componentbootstrap-modal

Using a custom modal in vue, 2 times inside another component renders only the first component data


So the problem is that I am working in a Laravel Vue application both are different application. The problem I am facing is that I have a created a component for modal using bootstrap in VUE JS. And I am using that modal to 2 different components that is creating a data and for editing that data. So I created 2 different components that is for createdata and editdata and called the modal component in both of these. And these 2 component is called in another component that is viewAllData. Createdata components works fine but when rendering Editdata component it renders the CreateData component fields, I dont know why. How can I do this other way.

Below is the code that I using

For Modal:

<template>
  <button
    type="button"
    :class="btnClass"
    data-bs-toggle="modal"
    data-bs-target="#staticBackdrop"
  >
    <slot name="btn-title"></slot>
  </button>

  <div
    class="modal fade"
    id="staticBackdrop"
    data-bs-backdrop="static"
    data-bs-keyboard="false"
    tabindex="-1"
    aria-labelledby="staticBackdropLabel"
    aria-hidden="true"
    ref="theModal"
  >
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h1 class="modal-title fs-5" id="staticBackdropLabel">
            <slot name="dialog-header"></slot>
          </h1>
          <button
            type="button"
            class="btn-close"
            data-bs-dismiss="modal"
            aria-label="Close"
          ></button>
        </div>
        <div class="modal-body">
          <slot></slot>
        </div>
        <div class="modal-footer">
          <button
            type="button"
            class="btn btn-secondary"
            data-bs-dismiss="modal"
          >
            Close
          </button>
          <button type="button" class="btn btn-primary" @click="success()">
            Ok
          </button>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { Modal } from "bootstrap";
export default {
  props: ["btnClass"],
  emits: ["on-success"],
  data() {
    return {
      theModal: null,
    };
  },
  methods: {
    success() {
      this.$emit("on-success", this.theModal);
    },
  },
  mounted() {
    this.theModal = new Modal(this.$refs.theModal);
  },
};
</script>

Below is the createdata component

<template>
  <backdrop-dialog-close-success
    btnClass="btn btn-success"
    @onSuccess="onSuccess"
  >
    <template #btn-title>Add New</template>
    <template #dialog-header>Add New Company</template>
    <template #default>
      <form>
        <div class="mb-3">
          <label for="companyName" class="form-label">Company Name</label
          ><asterisk-symbol />
          <input
            type="text"
            class="form-control"
            id="companyName"
            aria-describedby="emailHelp"
            v-model="companyName"
          />
        </div>
      </form>
    </template>
  </backdrop-dialog-close-success>
</template>
<script>
import BackdropDialogCloseSuccess from "../UI/BackdropDialogCloseSuccess.vue";
import AsteriskSymbol from "../misc/AsteriskSymbol.vue";
export default {
  components: {
    BackdropDialogCloseSuccess,
    AsteriskSymbol,
  },
  emits: ["on-success"],
  data() {
    return {
      companyName: "",
    };
  },
  methods: {
    onSuccess(modalInstance) {
      const formBody = { company_name: this.companyName };
      this.axios
        .post(
          this.$Constants.BaseUrl + this.$Constants.ApiEndpoints.AC_COMPANY,
          formBody
        )
        .then((res) => {
          if (res.status === 200) {
            console.log(res);
            this.$Methods.toastSuccess(res.data.message);
            modalInstance.hide();
            this.$emit("on-success");
          }
        })
        .catch((err) => {
          this.$Methods.toastError(err.response.data.message);
        });
      modalInstance.hide();
    },
  },
};
</script>

Below is the Editdata component

<template>
  <BackdropDialogCloseSuccess btnClass="btn btn-outline-warning">
      <template #btn-title>Edit</template>
      <template #dialog-header>Edit Company</template>
    <template #default>Tests</template>
  </BackdropDialogCloseSuccess>
</template>
<script>
import BackdropDialogCloseSuccess from "../UI/BackdropDialogCloseSuccess.vue";
export default {
  components: { BackdropDialogCloseSuccess },
};
</script>

Below is viewalldata component

<template>
  <TableView>
    <template #card-header>
      <h3 class="card-title">All Air-Conditioners</h3>
      <div class="row">
        <div class="ml-auto col-2">
          <add-new-company @on-success="fetchCompanyData"></add-new-company>
        </div>
      </div>
    </template>
    <template #table-header>
      <th>Sr No.</th>
      <th>Compnay Name</th>
      <th>AC Count</th>
      <th>Actions</th>
    </template>
    <template #default>
      <tr v-for="(company, index) in companyDetails" :key="company.company_id">
        <td>{{ index + 1 }}</td>
        <td>{{ company.company_name }}</td>
        <td>{{ company.ac_count }}</td>
        <td>
          <EditCompany></EditCompany>
          <!-- <span @click="editHandler(company.company_id)" class="show-pointer">
            <i
              class="fas fa-edit p-2"
              style="color: rgb(255, 153, 0); font-size: 22px"
            ></i>
          </span> -->
          <span @click="deleteHandler(company.company_id)" class="show-pointer">
            <i class="fas fa-trash p-2" style="color: red; font-size: 22px"></i>
          </span>
        </td>
      </tr>
    </template>
  </TableView>
  <!-- Button trigger modal -->
</template>
<script>
import TableView from "../UI/TableView.vue";
import AddNewCompany from "./AddNewCompany.vue";
import EditCompany from "./EditCompany.vue";
export default {
  components: { TableView,AddNewCompany, EditCompany},
  data() {
    return {
      companyDetails: [],
    };
  },
  methods: {
    fetchCompanyData() {
      this.axios
        .get(this.$Constants.BaseUrl + this.$Constants.ApiEndpoints.AC_COMPANY)
        .then((res) => {
          if (res.status === 200) {
            this.companyDetails = res.data.data;
          }
        })
        .catch((err) => {
          this.$Methods.toastError(err.response.data.message);
        });
    },
    editHandler(id){
      console.log(id);
    },
    deleteHandler(){

    }
  },
  mounted() {
    this.fetchCompanyData();
  },
};
</script>

when clicking on "add new" button Correct Output This works fine

But when clicking on "edit" button" wrong output This renders add data fields why I dont understand, And how to render different data

I want to render different data to that modal in similar way

Edit

Here is the Sandbox Code where you can check the bug. Its not the full code replica but the bug still persist


Solution

  • As you are using the same modal for edit and add company options, you need to identify each modal separately, so that clicking the button shows that specific modal.

    Currently, when add and edit modals renders in the DOM, all the modals have same ids so when clicking any button(add or edit), the first matched modal gets active and in your case its the add modal.

    To solve this you need to add unique ids in each modal and the same unique target on the button, so that clicking specific button enables the correct modal.

    Pass a unique key prop to the modal component and use its value to generate the target for the button and id for the modal. Pass this unique key prop on both the edit and add buttons.

    // Props passing for add and edit component
    <add-new-company @on-success="fetchCompanyData" uniqueKey="addData"/>
    <EditCompany :uniqueKey="company.company_id"/>
    
    
    // Add a method to generate unique target value for the button
    <button
      type="button"
      :class="btnClass"
      data-bs-toggle="modal"
      :data-bs-target="getTarget()"
    > ... </button>
    
    // Add the unique key passed as prop to the modal div
    <div
      class="modal fade"
      :id="uniqueKey"
      data-bs-backdrop="static"
      data-bs-keyboard="false"
      tabindex="-1"
      aria-labelledby="staticBackdropLabel"
      aria-hidden="true"
      ref="theModal"
    > ... </div>
    
    // Method for generating the target for the button
    getTarget() {
      return `#${this.uniqueKey}`
    }