'Multiple of the same component spawning the same modal on the same page?

I'm using Vue, and I have a component that outputs a button. That button opens a modal, also part of that component. This works.

If I have multiple of those buttons on the page then the modal is spawned twice. I understand why, but that's not what I want to happen.

Below is the code:

<template>    
    <div>
        <button @click="$bvModal.show('edit-profile-modal')" class="btn btn-sm btn-secondary mt-3">Edit</button>

        <b-modal id="edit-profile-modal" ref="editProfileModal" :ok-only="true">
            <template #modal-title>
                Edit your profile
            </template>
            <div class="d-block text-center">
                
            </div>
        </b-modal>
    </div>
</template>

<script>
    export default {
        props: {
        },

        data() {
            return {};
        },

        mounted() {
        },

        computed: {
        },

        methods: {
        }
    }
</script>

Is there a way to make this modal totally unique, so it isn't duplicated? It will always have the same content no matter what button is pressed.

The other questions on StackOverflow around this problem are focussed on the pattern of tabled data with 'Edit' buttons next to a row that spawns a modal with a form in it to edit that data - that's not what I'm trying to achieve. The modal is always the same and will always have the same data in it. I want to achieve a single component that I can drop in anywhere to allow a user to open this modal, so the solution isn't to put the modal outside of this component.

Thanks



Solution 1:[1]

The modal must be in its own component, or it will inevitably be duplicated every time you write it in the template. Create a component for the modal which you can import just once into App.vue. You can toggle it with a Vuex state.

Modal.vue

<b-modal>
...
</b-modal>

App.vue

<template>
  <div>
    ...
    <Modal v-show="showModal" />
  </div>
</template>
computed: {
  showModal() {
    return this.$store.state.showModal;
  }
}

store.js

state: {
  showModal: false
},
mutations: {
  toggleModal(state, isShown) {
    state.showModal = isShown;
  }
}

Now from any component you can use the mutation to show the modal:

methods: {
  showModal() {
    this.$store.commit('toggleModal', true);
  }
}

Solution 2:[2]

I know this is a bit old, but I stumbled upon the question while fiddling around with Bootstrap Vue's Modals and thought I'd add in an alternate solution.

BvModal identifies templates by id, and when the show function is called, it will display ALL matching templates tagged with that ID. So knowing that, all we have to do is make the IDs unique!

A sneaky way of doing this is to utilise the uid that Vue assigns to it's components:

<template>    
<div>
    <button @click="$bvModal.show(`edit-profile-modal-${_uid}`)" class="btn btn-sm btn-secondary mt-3">Edit</button>

    <b-modal :id="`edit-profile-modal-${_uid}`" ref="editProfileModal" :ok-only="true">
        <template #modal-title>
            Edit your profile
        </template>
        <div class="d-block text-center">
            
        </div>
    </b-modal>
</div>

Note that we're using template literals for both the $bvModal.show function and the id prop of the b-modal. You could just as easily use "'edit-profile-modal-' + _uid" if that looks nicer to you.

This will let you have any number of the same component on the page, without BvModal getting confused as to which modal belongs to which component and without having to create a custom component.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 TerriblePotato