简体   繁体   中英

Open a modal from a button in another component Vue JS

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 class="modal fade" ref="exampleModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
          <button type="button" class="btn-close" @click="modal.hide()" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          ...
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" @click="modal.hide()">Close</button>
          <button type="button" class="btn btn-primary">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 class="container ml-5 mt-3 border-1 rounded pl-5 pt-5 pr-5">
          <div class="row mb-4">
             <div class="col-md-6 mb-4" >
          <label for="test" class="label-form">Titre</label>
          <input autocomplete="off" placeholder="Rechercher par le titre du test" v-model="testName" type="text"  id="test" class="form-control">
          </div>
             <div class="col-md-6 mb-4">
            <label for="candidat" class="label-form">Candidat</label>
          <select  v-model="CandidateName" class="form-select" id="candidat">
            <option  value="" selected>Tous les candidats</option>
          </select>
          </div>
             </div>
             <div class="row">
               <div class="col-12 mb-3">
                 <div class="col-12 mb-4">
                   <div class="col-sm-6 col-12 d-inline-block">
                     <!-- Modal is shown on this button's click !-->
                    <button  type="button" @click="this.$emit('clickDone')" class=" btn btn-primary col-12 col-sm-11 mb-sm-0 mb-sm-0 mb-4 float-sm-left"><i class="fa-solid fa-file-circle-plus"></i> Ajouter Un Test</button>
                    </div>
                    <div class="col-sm-6 col-12 d-inline-block">
                      <button  @click="deleteTests" type="button" class=" btn btn-primary col-sm-12 col-12" v-if="false"><i class="fa-solid fa-file-circle-minus"></i>  Supprimer Le(s) Test(s)</button>
                    </div>
                  </div>
              <button id="searchTest" @click="searchBy" class="col-12 btn btn-primary" ><i class="fas fa-search"></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>

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 class="container ml-5 mt-3 border-1 rounded pl-5 pt-5 pr-5"> <div class="row mb-4"> <div class="col-md-6 mb-4" > <label for="test" class="label-form">Titre</label> <input autocomplete="off" placeholder="Rechercher par le titre du test" v-model="testName" type="text" id="test" class="form-control"> </div> <div class="col-md-6 mb-4"> <label for="candidat" class="label-form">Candidat</label> <select v-model="CandidateName" class="form-select" id="candidat"> <option value="" selected>Tous les candidats</option> </select> </div> </div> <div class="row"> <div class="col-12 mb-3"> <div class="col-12 mb-4"> <div class="col-sm-6 col-12 d-inline-block"> <.-- Modal is shown on this button's click.--> <button type="button" @click="this:$emit('clickDone')" class=" btn btn-primary col-12 col-sm-11 mb-sm-0 mb-sm-0 mb-4 float-sm-left"><i class="fa-solid fa-file-circle-plus"></i> Ajouter Un Test</button> </div> <div class="col-sm-6 col-12 d-inline-block"> <button @click="deleteTests" type="button" class=" btn btn-primary col-sm-12 col-12" v-if="false"><i class="fa-solid fa-file-circle-minus"></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" class="col-12 btn btn-primary" ><i class="fas fa-search"></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" class="modal fade" ref="exampleModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Modal title</h5> <button type="button" class="btn-close" @click="hideModal" aria-label="Close"></button> </div> <div class="modal-body">... </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" @click="hideModal">Close</button> <button type="button" class="btn btn-primary">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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM