在 Composition API 中使用 pinia 操控 modal
之前寫過一篇在 《在 Composition API 中使用 props 與 emit 操控 modal》的筆記,那時是使用 props 跟 emit 處理外部元件與內部元件之間資料傳遞的問題。
但是當有兩個以上的 modal/彈跳視窗 的時候,第一個 modal 是開啟狀態,這時觸發第二個 modal 開啟,同時第一個 modal 要關閉。這種情形下,兩個內部元件彼此要傳遞訊息變得十分複雜,這次我試著用 pinia 來管理兩個 modal 的狀態,狀態就不再儲存在兩個 modal 元件內部。
任務說明
在 pinia 中有 isFirstOpen
跟 isSecondOpen
兩個變數,用來管理 2 個元件的開啟與關閉,預設都是 false
, 內部元件則用 v-if 控制元件的顯示與否,在 false
狀態,兩個元件皆為關閉,當有一個元件被開啟,則另外一個元件的狀態則會轉為 false(預設也是 false)。
假設第一個元件是開啟狀態,isFirstOpen
的狀態為 true
,第二個元件的狀態isSecondOpen
為 false
。這時去開啟第二個元件,isSecondOpen
轉為 true
,則isFirstOpen
轉為 false,第一個元件就會關閉,反之亦然。這樣就可以達到切換2個 modal 的目的。
檔案結構如下:
main.js 引入 pinia
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount("#app");
pinia 中 modalControl.js 的設定
stores/modalControl.js
import { defineStore } from "pinia";
export default defineStore("modalControl", {
state: () => ({ name: "胖胖", isFirstOpen: false, isSecondOpen: false })
});
app.vue 中的設定
把兩個元件 import 進來,當成子元件。
把 pinia 中的狀態及方法匯入。
import { storeToRefs } from "pinia"
import modalControl from "@/stores/modalControl.js";
設定開啟及切換 modal 的方法
const openModal = function () {
isFirstOpen.value = true;
isSecondOpen.value = false;
};
// 開啟第二個 modal,關閉第一個 modal
const openSecond = function () {
isSecondOpen.value = true;
isFirstOpen.value = false;
};
- 將兩個按鈕綁上開啟 modal 的方法。
<button type="button" class="btn" @click="openModal">showModal</button>
<button type="button" class="btn" @click="openSecond">showModal2</button>
app.vue
<template>
<div class="up">
<button type="button" class="btn" @click="openModal">showModal</button>
<button type="button" class="btn" @click="openSecond">showModal2</button>
</div>
<!-- <Modal :open="isOpen" @close="isOpen = !isOpen"> -->
<Modal>
<p>
{{ name }}
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quo molestiae
consectetur blanditiis aspernatur velit autem labore amet
</p>
</Modal>
<SecondModal>
<p>
{{ name }}
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quo molestiae
consectetur blanditiis aspernatur velit autem labore amet
</p>
</SecondModal>
</template>
<script>
import { ref } from "vue";
// 第一個元件
import Modal from "./components/ModalView.vue";
// 第二個元件
import SecondModal from "./components/SecondModal.vue";
// pinia 中共用的狀態
import modalControl from "@/stores/modalControl.js";
// 使用 pinia 中資料的方法
import { storeToRefs } from "pinia";
// import HelloWorldVue from "./components/HelloWorld.vue";
export default {
components: { Modal, SecondModal },
setup() {
// 把 pinia 中元件共用的狀態,賦值給 modaltest 這個變數
const modaltest = modalControl();
// 解構
const { name, isFirstOpen, isSecondOpen } = storeToRefs(modaltest);
// const isOpen = ref(false);
// 開啟第一個 modal,關閉第二個 modal
const openModal = function () {
isFirstOpen.value = true;
isSecondOpen.value = false;
};
// 開啟第二個 modal,關閉第一個 modal
const openSecond = function () {
isSecondOpen.value = true;
isFirstOpen.value = false;
};
return {
isFirstOpen,
modalControl,
name,
isSecondOpen,
openModal,
openSecond,
};
},
};
</script>
<style>
.up {
z-index: 500;
cursor: pointer;
}
</style>
2 個 modal 元件的設定
ModalView.vue
<template>
<transition name="fade">
<div class="vue-modal" v-if="isFirstOpen">
<transition name="drop-in">
<div class="vue-modal-inner" v-if="isFirstOpen">
<div class="vue-modal-content">
<slot />
{{ name }} {{ isOpen }}
<button type="button" @click="isFirstOpen = false">close</button>
</div>
</div>
</transition>
</div>
</transition>
</template>
<script>
import { onMounted, onUnmounted } from "vue";
import modalControl from "@/stores/modalControl.js";
import { storeToRefs } from "pinia";
export default {
setup() {
const modaltest = modalControl();
const { name, isFirstOpen } = storeToRefs(modaltest);
return {
modaltest,
name,
isFirstOpen,
};
},
};
</script>
SecondModal.vue
<template>
<transition name="fade">
<div class="vue-modal" v-if="isSecondOpen">
<transition name="drop-in">
<div class="vue-modal-inner" v-if="isSecondOpen">
<div class="vue-modal-content">
<slot />
{{ name }} {{ isOpen }}
<button type="button" @click="isSecondOpen = false">close</button>
</div>
</div>
</transition>
</div>
</transition>
</template>
<script>
import modalControl from "@/stores/modalControl.js";
import { storeToRefs } from "pinia";
export default {
setup(props, { emit }) {
const modaltest = modalControl();
const { name, isSecondOpen } = storeToRefs(modaltest);
return {
modaltest,
name,
isSecondOpen,
};
},
};
</script>