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

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

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

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

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

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

## 1. モーダルを開く親コンポーネントを実装 (MainComponent.vue)
このコンポーネントには、モーダルを開くためのボタンがあり、クリックすると `SlideupModal.vue` に `ModalComponent.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 を渡す**
**汎用的なモーダル** を実装可能

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