在前端視覺 UI 上常常有些情境是,點擊某個按鈕後觸發一個資料輸入面板,如果是使用 Bootstrap 5 當作網頁視覺框架,這樣的介面互動可以藉由 Bootstrap 的 Modal(互動視窗) 元件來實踐。
先到 Bootstrap 的官方文件玩玩看!
https://bootstrap5.hexschool.com/docs/5.1/components/modal/
官方 Modal HTML 程式碼
<!-- 觸發 Modal 的按鈕 -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
Launch demo modal
</button>
<!-- Modal 本體 -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" 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" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>以上的 Modal 互動模式已經被封裝在 Bootstrap 所提供的程式裡面,藉由 data-bs- 開頭的屬性來操作,如果要在 VUE3 把 Modal 做成元件來使用,則需要使用 Bootstrap 所提供的 JavaScript 方法:
var myModal = new bootstrap.Modal(document.getElementById('myModal'), options)今天就讓我來整理一下如何在 VUE 裡面使用 Bootstrap 的 Modal 元件,不過在這之前我們要先做好兩件事。
- 假設使用 VITE 當腳手架,透過 NPM 安裝好 Bootstrap,並且在 main.js 引入 Bootstrap 與 Bootstrap CSS。
- 理解在 VUE 裡面透過 ref 取得 DOM 元素的方法。
以下分為 Option API 與 Composition API 的 setup 模式來進行說明:
Option API 的 BS Modal
Step1 : 我們先把 App.vue 當成父層,放入觸發 Modal 的按鈕。
<template>
<!-- data-bs-toggle="modal" 要拿掉 -->
<button
type="button"
class="btn btn-primary"
data-bs-target="#exampleModal"
>
Launch demo modal
</button>
</template>Step2 :建立一個 modal.vue 檔案,在 template 放入 Modal 的本體,並且在整個 Modal 元件的元素最外層,加上ref="exampleModal"。
<div class="modal" tabindex="-1" ref="exampleModal"><div class="modal" tabindex="-1" ref="exampleModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<!-- data-bs-dismiss="modal" -->
<button type="button" class="btn btn-secondary" @click="hideModal()">
Close
</button>
<button type="button" class="btn btn-primary" @click="hideModal()">
Save changes
</button>
</div>
</div>
</div>
</div>Step3 :在 modal.vue 的 script 中 import bootstrap 的 modal 方法。
import Modal from 'bootstrap/js/dist/modal';Step4 :然後在 data 建立一個 modal 的空物件,並且在 mounted 生命週期中,使用 import 進來的 Modal 方法,指向 ref="exampleModal",並賦予給modal 空物件 。
export default {
data() {
return {
modal: {},
};
},
mounted() {
// 透過refs的方式,把 DOM 元素指向外層的modal
// 而 myModal 再指回 data 裡的變數
this.modal = new Modal(this.$refs.exampleModal);
// 用show呈現在畫面上
// this.modal.show()
},
};Step5 :在 methods 中建立打開 modal 跟關閉 modal 的方法:
showModal() {
this.modal.show();
},
hideModal() {
this.modal.hide();
},完整的 modal.vue 程式碼:
<template>
<div class="modal" tabindex="-1" ref="exampleModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<!-- data-bs-dismiss="modal" -->
<button type="button" class="btn btn-secondary" @click="hideModal()">
Close
</button>
<button type="button" class="btn btn-primary" @click="hideModal()">
Save changes
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import Modal from 'bootstrap/js/dist/modal';
export default {
data() {
return {
modal: {},
};
},
methods: {
showModal() {
this.modal.show();
},
hideModal() {
this.modal.hide();
},
},
mounted() {
// 透過refs的方式,把 DOM 元素指向外層的modal
// 而 myModal 再指回 data 裡的變數
this.modal = new Modal(this.$refs.exampleModal);
// 用show呈現在畫面上
// this.modal.show()
},
};
</script>Step6:最後在 App.vue 中 import modal.vue,然後在外層的button加上 :
@click="$refs.exampleModal.showModal()" <button
type="button"
class="btn btn-primary"
@click="$refs.exampleModal.showModal()"
>
Launch demo modal
</button>option api 範例:https://stackblitz.com/edit/vitejs-vite-us3dki?file=src%2FApp.vue
Composition API 的 BS Modal
在Composition API 中,我以 Setup function 來做說明。一樣是以 App.vue 當作父層,modal.vue 當作子元件。
Step1 : 在 modal.vue 的 template 中放入 modal 本體。在最外層的 div 加入 ref=”modal”。
<div class="modal" tabindex="-1" ref="modal">modal.vue:
<template>
<div class="modal" tabindex="-1" ref="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary" @click="myModal_hide()">
Save changes
</button>
</div>
</div>
</div>
</div>
</template>Step2 : 在 <script setup>中 import bootstrap 的 Modal 方法,並建立 modal 與 myModal 的 ref,modal呼應前面在 modal 本體外層 div 放入的 ref=”modal”:
<script setup>
import { onMounted, ref } from 'vue';
import Modal from 'bootstrap/js/dist/modal';
const modal = ref(null);
const myModal = ref(null);Step3 : 在 onMounted 中使用 bootstrap 的 Modal 方法指向 Modal 本體的ref modal.value,並賦值給 myModal.value。
onMounted(() => {
myModal.value = new Modal(modal.value);
});Step4 : 建立開啟與關閉 modal 的方法:
const myModal_show = () => {
alert('hi');
myModal.value.show();
};
const myModal_hide = () => {
myModal.value.hide();
};Step5 : 使用 defineExpose 把封裝在 setup 中的 myModal_show 跟 myModal_hide這兩個方法曝露給父元件。
defineExpose({
myModal_show,
myModal_hide,
});
</script>完整的 modal.vue 程式碼:
<template>
<div class="modal" tabindex="-1" ref="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
@click="myModal_hide()"
>
Close
</button>
<button type="button" class="btn btn-primary" @click="myModal_hide()">
Save changes
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import Modal from 'bootstrap/js/dist/modal';
const modal = ref(null);
const myModal = ref(null);
onMounted(() => {
myModal.value = new Modal(modal.value);
});
const myModal_show = () => {
alert('hi');
myModal.value.show();
};
const myModal_hide = () => {
myModal.value.hide();
};
defineExpose({
myModal_show,
myModal_hide,
});
</script>
Step6 : 在父元件引入 modal.vue 元件,並建立一個 ref 指向 modal 元件。
<template>
<!-- data-bs-toggle="modal" 要拿掉 -->
<button type="button" class="btn btn-primary">
Launch demo modal
</button>
<MeModal ref="XsModal"></MeModal>
</template>
<script setup>
import { ref } from 'vue';
import MeModal from './components/modal.vue';
const XsModal = ref(null);
</script>
step7 : 在 button 上加入 @click=”XsModal.myModal_show()”
完整的父元件程式碼:
<template>
<!-- data-bs-toggle="modal" 要拿掉 -->
<button type="button" class="btn btn-primary" @click="XsModal.myModal_show()">
Launch demo modal
</button>
<MeModal ref="XsModal"></MeModal>
</template>
<script setup>
import { ref } from 'vue';
import MeModal from './components/modal.vue';
const XsModal = ref(null);
</script>
setup 範例:https://stackblitz.com/edit/vitejs-vite-lh2jvc?file=src%2Fcomponents%2Fmodal.vue
參考資料:
