menu

Questions & Answers

Boostrap modal renders multiple times

In my Table component, I'm using Vue Bootstrap's b-table component to create a table, which retrieves its' data from an external JSON file through Vuex. Now I also have another component, Actions, which is rendered on each row of the table. This component contains an edit button which is supposed to open a modal when clicked.

The problem is that whenever I click the edit button, 4 modals come up one on top of another. The issue seems to lie in the number of rows rendered, because in the JSON file, there are 4 objects, each of which contains the student's name, date of birth and so on. When I get rid of three of these objects, the modal only renders once. My conclusion is that the modal is rendering 4 times, for each row, but I have no idea how to fix this.

Here's the Table and Actions component:

<script>
import Actions from "./Actions.vue"

export default {
    data() {
        return {
            fields: [
                'index',
                'full_name',
                { key: "date_of_birth", label: 'Date of Birth' },
                'municipality',
                { key: "action", label: 'Action' }
            ],
            // tableItems: this.$store.state.registeredStudents.registeredStudents
        }
    },

    components: {
        Actions
    },

    methods: {
        generateIndex() {
            return Math.floor(1000000 * Math.random()).toString().slice(0, 6);
        }
    },

    computed: {
        rows() {
            return this.tableItems.length
        },

        tableItems() {
            const registeredStudents = this.$store.state.registeredStudents.registeredStudents

            return registeredStudents.map(student => ({
                index: this.generateIndex(), ...student
            }))
        }
    },

    
}
</script>

<template>
    <b-table :fields="fields" :items="tableItems" :per-page="perPage" :current-page="currentPage" responsive="sm" primary-key="index"
        striped hover>
        <template #cell(action)="data">
            <Actions/>
        </template>
    </b-table>
</template>
<script>
import { BIconPencilFill, BIconTrashFill } from 'bootstrap-vue';

export default {

}
</script>

<template>
    <div>
        <b-button variant="primary" class="mx-1 p-1" v-b-modal.edit-student>
            <b-icon-pencil-fill></b-icon-pencil-fill>
        </b-button>
        <b-modal id="edit-student" title="Edit student info">
            <p class="my-4">Hello from modal!</p>
        </b-modal>
        <b-button variant="danger" class="mx-1 p-1">
            <b-icon-trash-fill></b-icon-trash-fill>
        </b-button>
    </div>
</template>
Answers(1) :

The problem here is most likely because your id's are not unique. For each row of your table you are generating the following;

<b-modal id="edit-student" title="Edit student info">
    <p class="my-4">Hello from modal!</p>
</b-modal>

with the same ID.

I think the best approach here would be to include the Actions in the Table component. You don't need the extra component since it has no internal logic or props.

Table.vue

data() {
        return {
            fields: [
                'index',
                'full_name',
                { key: "date_of_birth", label: 'Date of Birth' },
                'municipality',
                { key: "action", label: 'Action' }
            ],
            lastClickedRowIndex: -1
        }
    },
    ...

<template>
    <b-table :fields="fields" :items="tableItems" :per-page="perPage" :current-page="currentPage" responsive="sm" primary-key="index"
        striped hover>
        <template #cell(action)="data">
          <b-button variant="primary" class="mx-1 p-1" @click="() => (lastClickedRowIndex = data.index)" v-b-modal.edit-student>
            <b-icon-pencil-fill></b-icon-pencil-fill>
          </b-button>
          <b-button variant="danger" class="mx-1 p-1">
            <b-icon-trash-fill></b-icon-trash-fill>
          </b-button>
        </template>
    </b-table>
    <b-modal id="edit-student" title="Edit student info">
        <p class="my-4">Hello from modal!</p>
        <!-- Dynamically change contents here based on lastClickedRowIndex -->
    </b-modal>
</template>

I apologise if any syntax is incorrect I've only used Vue 3.