Ben Kearney
Ben Kearney

Reputation: 41

Managing which bootstrap-vue modal is open through vuex state

I am building an application that requires sequencing multiple bootstrap-vue modals. Specifically, a button in a nested component should open the 'manage' modal. The 'manage' modal contains buttons that, when clicked, should close the 'manage' modal and open another modal (e.g. 'edit', 'add', 'complete' etc).

Rather than passing props/emitting events up and down so that these can be triggered from different nested components, I would like to have one value in my store, this.$store.state.general.activeModal, that determines which modal is showing (if any)

Question: How can I create a set of modals in my main app page that render if a value in the state is changed?

My main app will look like this:

<template>
   <app-stuff />
   <b-modal id="modal1" />
   <b-modal id="modal2" />
   <b-modal id="modal3" />
</template>

e.g. modal1 should show when this.$store.state.general.activeModal gets set to 'modal1' and close when the value is changed to something else.

I tried creating a computed variable "showModal1" that is true when store.etc.activeModal=='modal1'and false otherwise, then using v-modal="showModal1" to show/hide the modal, but it just ended up creating two copies of the modal every time the value in the store matched (apparently computed values trigger twice when a value in store is changed?)

Any help would be great!

Upvotes: 3

Views: 3930

Answers (3)

Tomhah
Tomhah

Reputation: 386

You could create a computed property for the visibility of each modal, for example:

computed: {
  modal1Visible() {
    return this.$store.state.activeModal === 'modal1';
  }
}

Then set the visible property of the b-modal:

<b-modal :visible="modal1Visible" id="modal1">

To handle closing modals, you can use the hide event in combination with a store action or mutation which sets the value of this.$store.state.activeModal, for example:

<b-modal :visible="modal1Visible"
         @hide="$store.commit('activeModalSet', null)"
         id="modal1"
</b-modal>

This means if you close the modal via the v-b-modal directive, the close button, or some other method:

  1. The modal will emit a hide event
  2. The activeModalSet mutation will be triggered, setting this.$store.activeModal to null
  3. The modal1Visible computed property will now evaluate to false
  4. The visible property of the modal will be false, so the modal will be hidden.

Upvotes: 4

clay
clay

Reputation: 6017

Although not bootstrap-vue, we have had success with Bootstrap Modals with the simple v-if directive. The modal only shows/renders if the condition is true.

Using Vuex, you can have a computed property for activeModal and a v-if on each modal for v-if="activeModal == 'modalName'". In our modals, we used Vue lifecycle mounted to show our modal, and register an emit (bootstrap-vue might handle that differently...)

$('#' + this.modalId).on('hide.bs.modal', () => { this.$emit('closeModal') //this would set the v-if in parent Component to false, un-rendering the modal component })

Upvotes: 0

Ekushisu
Ekushisu

Reputation: 461

I would suggest you to use the b-modal component's methods : .show() and .hide() inside a watcher which will catch your state mutations :

<template>
    <div>
        <b-modal ref="modal1" id="modal1"></b-modal>
        <b-modal ref="modal2" id="modal2"></b-modal>
        <b-modal ref="modal3" id="modal3"></b-modal>
    </div>
</template>

Don't pay attention to mapGetters, you have to watch your store getters/state. Supposed here that activeModal is your state value.

computed : {
    ...mapGetters([
        'activeModal'
    ])
},
watch : {
    activeModal(newActiveModal, oldActiveModal) {
        // If an old value was setted, we want to hide it in anyway.
        if (oldActiveModal) this.$refs[oldActiveModal].hide()
        // If the new value is falsy, do nothing
        if (!newActiveModal) return

        this.$refs[newActiveModal].show()
    }

}

Upvotes: 0

Related Questions