在 Composition API 中使用 props 與 emit 操控 modal

在前端工程師的日常中,modal/ 彈出視窗 是常常會遇到的工作情境。畫面大概就是外部元件有一個按鈕,按下去會有一個對話框(內部元件)彈出來,對話框上有個按鈕,點擊後對話框就會關閉。

如果前端框架是 Vue 的話,一般我們會在外部元件定義一個布林值,預設為 false ,按下外部的按鈕後,布林值會變為 true

  • 開啟:在內部元件 modal 用 props 接收外部元件的布林值,綁上 v-if= props 接收到的布林值。這樣就可以由外部元件控制內部元件的開啟。

  • 關閉:內元件 modal 可以由元件內部的按鈕關閉,按下按鈕後以 emit 傳送一個事件去觸發外部元件反轉布林值,達成元件的關閉。

App.vue(外部元件)

<template>
  <button @click="isOpen = true">showModal</button>
  <!-- 外部元件中插入內部元件 modal -->
  <!-- props 口訣:先內後外 :open="isOpen"-->
  <!-- emit 口訣:先內後外  @close="isOpen = !isOpen"-->
  <Modal :open="isOpen" @close="isOpen = !isOpen">
    <p>
      Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quo molestiae
      consectetur blanditiis aspernatur velit autem labore amet
    </p>
  </Modal>
</template>

<script>
import { ref } from "vue";
import Modal from "./components/ModalView.vue";
// import HelloWorldVue from "./components/HelloWorld.vue";
export default {
  components: { Modal },
  setup() {
    // 由 isOpen 的 true 或 false 控制 modal 的開關
    const isOpen = ref(false);

    return {
      isOpen,
    };
  },
};
</script>

<style>
</style>

/components/ModalView.vue(內部元件)

<template>
  <transition name="fade">
    <!-- 控制背景 -->
    <div class="vue-modal" v-if="open">
      <transition name="drop-in">
        <!-- 彈跳視窗本體 props 為 open,所以 v-if="open" -->
        <div class="vue-modal-inner" v-if="open">
          <div class="vue-modal-content">
            <slot />
            <!-- @click="close" 觸發 emit 事件,傳送至外層切換 isOpen -->
            <button type="button" @click="close">close</button>
          </div>
        </div>
      </transition>
    </div>
  </transition>
</template>

<script>
import { onMounted, onUnmounted } from "vue";
export default {
  props: {
    open: {
      type: Boolean,
      requires: true,
    },
  },
  // peops 這個參數一定要帶到,才能使用 {emit}
  setup(props, { emit }) {
    const close = () => {
      emit("close");
    };
    const handleKeyup = (event) => {
      close();
    };
    onMounted(() => document.addEventListener("keyup", handleKeyup));
    onUnmounted(() => document.removeEventListener("keyup", handleKeyup));
    return { close };
  },
};
</script>
<style scoped>
*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.vue-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: auto;
  background-color: rgba(0, 0, 0, 0.04);
  z-index: 1;
}
.vue-modal-inner {
  max-width: 500px;
  margin: 2rem auto;
}
.vue-modal-content {
  position: relative;
  background-color: #fff;
  border: 1px solid rgba(0, 0, 0, 0.3);
  background-clip: padding-box;
  border-radius: 0.3rem;
  padding: 1rem;
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
.drop-in-enter-active,
.drop-in-leave-active {
  transition: all 0.3s easeout;
}
.drop-in-enter-from,
.drop-in-leave-to {
  opacity: 0;
  transform: translateY(-50px);
}
</style>

在 Composition API 中使用 props 與 emit 操控 modal
https://popeye-ux.github.io/2023/01/27/modal-props-emit/
作者
POPEYE
發布於
2023年1月27日
許可協議