0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue 3 で動的にコンポーネントを切り替えるモーダルの実装方法

Last updated at Posted at 2025-01-31

はじめに

Webアプリを開発する際、モーダルを使用してユーザーに追加情報を提供したり、フォーム入力を促したりすることがよくあります。

本記事では、Vue 3 の shallowRefref を使って 動的にコンポーネントを切り替えるモーダル の実装方法を紹介します。

「ボタンをクリックすると、指定したコンポーネントがモーダル内に表示される」という動作を実現していきます。

実装の概要

今回の実装では、以下の3つのコンポーネントを作成し、以下の流れでモーダルを動的に表示します。
MainComponent.vue(モーダルを開くボタンを持つコンポーネント)
SlideupModal.vue(モーダルコンポーネント)
ModalComponent.vue(モーダルで表示するコンポーネント)

流れ

  1. ボタンをクリック → callModal メソッドを実行。
  2. モーダルに表示するコンポーネントを指定 → childComponent にセット。
  3. モーダルのタイトルや props を設定。
  4. モーダルを開く。

1. モーダルを開く親コンポーネントを実装 (MainComponent.vue)

このコンポーネントには、モーダルを開くためのボタンがあり、クリックすると SlideupModal.vueModalComponent.vue を渡します。
実装例:

<template>
  <button
    @click=“callModal(‘modalComponent’, ‘モーダル‘, modalComponentProps)”
  >
    モーダルを開く
  </button>

  <SlideupModal
    :childComponent=“childComponent”
    :childProps=“childProps”
    :headingText=“headingText”
    :calledElement=“calledElement”
    ref=“slideupModal”
  />
</template>

<script setup>
import SlideupModal from ‘@/components/SlideupModal.vue’;
import ModalComponent from ‘@/components/ModalComponent.vue’;
import { shallowRef, ref, computed } from ‘vue’;

const modalInfo = computed(() => state.modalInfo);
const componentsOnModal = shallowRef({
  modalComponent: ModalComponent
});

const childComponent = shallowRef(null);
const childProps = ref({});
const headingText = ref(‘’);
const slideupModal = ref(null);
const calledElement = ref(null);

const callModal = (componentName, headingTitle, event) => {
  childComponent.value = componentsOnModal.value[componentName];
  childProps.value = { modalInfo: modalInfo.value };
  headingText.value = headingTitle;
  calledElement.value = event.currentTarget;
  slideupModal.value.openModal();
};
</script>

この実装のポイント:
SlideupModal は下部にポップアップ表示されるモーダル(slide-up modal) コンポーネント。
shallowRef を使い、モーダルで表示するコンポーネントを動的に変更。
componentsOnModal にモーダルで表示するコンポーネントを定義。
 childComponent.value = componentsOnModal.value[componentName] → ロードするコンポーネントを指定する (ModalComponent)。
 childProps.value = { modalInfo: modalInfo.value }modalInfo データを渡す
 headingText.value = headingTitle → モーダルのタイトルを設定。
callModal メソッドで コンポーネント名(modalComponent)・タイトル(モーダル)・props(modalComponentProps) をセット。
slideupModal.value.openModal() で モーダルを開く。

2. モーダルの基本コンポーネントを作成 (SlideupModal.vue)

まず、モーダルの共通コンポーネント SlideupModal.vue を作成します。
このコンポーネントは、childComponent という動的なコンポーネントを受け取り、モーダル内にレンダリングする仕組みになっています。
モーダル自体はシンプルにし、<component :is=“childComponent” /> を使って動的にコンポーネントを切り替えます。

<template>
  <div :class=“{ view: isView }” @click=“closeModal()“></div>
  <div :class=“{ view: isView }” ref=“modalContent”>
    <header>
      <h2>{{ headingText }}</h2>
      <button @click=“closeModal()“>×</button>
    </header>
    <div>
      <component :is=“childComponent” v-bind=“childProps || {}“></component>
    </div>
  </div>
</template>

<script setup>
import { ref } from ‘vue’;

const isView = ref(false);
const headingText = defineProps([‘headingText’]);
const childComponent = defineProps([‘childComponent’]);
const childProps = defineProps([‘childProps’]);

const openModal = () => {
  isView.value = true;
};

const closeModal = () => {
  isView.value = false;
};
</script>

この実装のポイント:
isView モーダルの表示/非表示状態を制御する。
:is=“childComponent” で 動的にコンポーネントを切り替え。
openModal()closeModal() で モーダルの表示/非表示を制御。

3. モーダルで表示するコンポーネントを作成(ModalComponent.vue)

モーダル内で表示する ModalComponent.vue を作成します。

4. 動作確認

・ボタンをクリックすると ModalComponent.vue がモーダル内に表示される。
・背景や閉じるボタンを押すとモーダルが閉じる。
・他のコンポーネントにも応用可能。

5. まとめ

この方法を使うことで、複数の異なるコンポーネントを動的に切り替えてモーダル内に表示できます。

shallowRef を活用して 動的にコンポーネントを切り替え
v-bind を使って props を渡す
汎用的なモーダル を実装可能

モーダルの再利用性を高めたい方は、ぜひ試してみてください!

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?