Home > Net >  Open a modal from a button in another component Vue JS
Open a modal from a button in another component Vue JS

Time:03-25

I have 2 components. One called TestSearchTool and another one called TestModal. I'd like to show my Modal when I click a button in the TestSearchTool component. These 2 components are siblings in the hierarchy so I didn't really find a way to pass the value between them ( there is vuex but I'd like to find a way without it ) I'm using Bootstrap 5 and Vue 3 to do this since Bootstrap Vue doesn't work on Vue 3.

Here is my TestModal component :

<template>
<div>
  <div  ref="exampleModal" tabindex="-1" aria-hidden="true">
    <div >
      <div >
        <div >
          <h5  id="exampleModalLabel">Modal title</h5>
          <button type="button"  @click="modal.hide()" aria-label="Close"></button>
        </div>
        <div >
          ...
        </div>
        <div >
          <button type="button"  @click="modal.hide()">Close</button>
          <button type="button" >Save changes</button>
        </div>
      </div>
    </div>
  </div>
  </div>
</template>
<script>
import { Modal } from 'bootstrap'
export default {
  name: "App",
  data: () => ({
    modal: null
  }),
  mounted() {
    this.modal = new Modal(this.$refs.exampleModal)
  }
};
</script>

Here is my TestSearchTool component :

<template>
  <div>
      <div >
          <div >
             <div  >
          <label for="test" >Titre</label>
          <input autocomplete="off" placeholder="Rechercher par le titre du test" v-model="testName" type="text"  id="test" >
          </div>
             <div >
            <label for="candidat" >Candidat</label>
          <select  v-model="CandidateName"  id="candidat">
            <option  value="" selected>Tous les candidats</option>
          </select>
          </div>
             </div>
             <div >
               <div >
                 <div >
                   <div >
                     <!-- Modal is shown on this button's click !-->
                    <button  type="button" @click="this.$emit('clickDone')" ><i ></i> Ajouter Un Test</button>
                    </div>
                    <div >
                      <button  @click="deleteTests" type="button"  v-if="false"><i ></i>  Supprimer Le(s) Test(s)</button>
                    </div>
                  </div>
              <button id="searchTest" @click="searchBy"  ><i ></i> Rechercher</button>
             </div>
             
          </div>
            
          </div>
  </div>
</template>

<script>
export default {
components:{
},
data(){
    return {
        testName:'',
        CandidateName:'',
        show:false,
    }
},
methods:{
},
}
</script>

<style scoped>
</style>

And here is the parent component (called Tests) :

<template>
  <div>
  
     <test-search-tool></test-search-tool>
     
  <test-modal></test-modal>
  </div>
</template>

<script>
import TestSearchTool from "../components/TestSearchTool.vue"
import TestModal from "../components/TestModal.vue"
export default {
components : {
    DashboardNavbar,
    TestSearchTool,
    TestModal,
},
mounted(){
      document.body.style.backgroundImage='url("")';
      document.body.style.background="white";
      
}
}
</script>

<style>

</style>

CodePudding user response:

As I know the goal of writing codes in components in simple words is to have special duties specific to that component. So when we define a "modal" component, we expect that it shows modal content for us. So in my opinion it is not correct to say modal component (here called TestModal.vue) and TestSearchTool.vue are siblings. You could use TestModal.vue every where in your app structure that you need a modal content to be shown. In other words the TestModal.vue component does not have any special content or logic to be the direct child of Tests.vue (the parent component).

With the above description I used TestModal.vue as child of TestSearchTool.vue and by using props and watch and emit capabilities of Vue, here is the codes of all 3 components:

TestSearchTool.vue:

<template>
  <div>
    <div >
      <div >
        <div  >
          <label for="test" >Titre</label>
          <input autocomplete="off" placeholder="Rechercher par le titre du test" v-model="testName" type="text"  id="test" >
        </div>
        <div >
          <label for="candidat" >Candidat</label>
          <select  v-model="CandidateName"  id="candidat">
            <option  value="" selected>Tous les candidats</option>
          </select>
        </div>
      </div>
      <div >
        <div >
          <div >
            <div >
              <!-- Modal is shown on this button's click !-->
              <button  type="button" @click="this.$emit('clickDone')" ><i ></i> Ajouter Un Test</button>
            </div>
            <div >
              <button  @click="deleteTests" type="button"  v-if="false"><i ></i>  Supprimer Le(s) Test(s)</button>
            </div>
          </div>
          <!-- *********************** -->
          <!-- this button is responsible for showing modal. you can use such a button in other parts of your app if you define "data" correctly -->
          <button id="searchTest" @click="dataModal = true"  ><i ></i> Rechercher</button>
        </div>

        <test-modal @closeModal="dataModal = false" :showModal="dataModal"></test-modal>
        <!-- *********************** -->
      </div>

    </div>
  </div>
</template>

<script>
import TestModal from "../components/TestModal.vue"
export default {
  name: "TestSearchTool",
  components:{
    TestModal
  },
  data(){
    return {
      testName:'',
      CandidateName:'',
      show:false,
      /* this data is used for showing modal */
      dataModal: false
    }
  }
}
</script>

<style scoped>
</style>

TestModal.vue:

<template>
  <div>

    <div id="myModal"  ref="exampleModal" tabindex="-1" aria-hidden="true">
      <div >
        <div >
          <div >
            <h5  id="exampleModalLabel">Modal title</h5>
            <button type="button"  @click="hideModal" aria-label="Close"></button>
          </div>
          <div >
            ...
          </div>
          <div >
            <button type="button"  @click="hideModal">Close</button>
            <button type="button" >Save changes</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { Modal } from 'bootstrap'
export default {
  name: "TestModal",
  data: () => ({
    modalInstance: null
  }),
  props: {
    showModal: {
      type: Boolean,
      default: false
    }
  },
  watch: {
    showModal(newValue, oldValue) {
      console.log(newValue);
      if (newValue === true) {
        this.modalActive();
      }
    }
  },
  methods: {
    modalActive: function () {
      this.modalInstance = new Modal(document.getElementById('myModal'), {
        target: "#my-modal",
        backdrop: "static"
      });
      this.modalInstance.show()
    },
    hideModal: function () {
      console.log("closed");
      this.modalInstance.hide();
      this.$emit('closeModal');
    }
  }
};
</script>

<style scoped>

</style>

Tests.vue:

<template>
  <div>
    <test-search-tool></test-search-tool>
  </div>
</template>

<script>
import TestSearchTool from "../components/TestSearchTool.vue"

export default {
  name: "Tests",
  components : {
    TestSearchTool
  }
}
</script>

<style scoped>

</style>

  • Related