<component>
の使用
1か所の要素から複数のモーダルを条件に応じて切り替えて表示させる方法
<component
:is="modalComponent"
:modalId="activeModal"
:studentId="selectedStudentStore.current.student_id"
:studentName="selectedStudentStore.current.student_name"
:recordDate="selectedStudentStore.current.date"
:subject="currentSubject"
:isOpen="true"
@close="closeModal"
@confirm="handleModalSave"
/>
ポイント1
Vue.js で <component>
というタグは 特別な予約タグ
で、動的にコンポーネントを切り替えるための専用タグなんです。
※つまり カスタムコンポーネント ではない
ポイント2
🔍 解説:
✅ :is
は Vue の構文
:is は v-bind:is
の省略形で、Vue コンポーネントや HTML タグを動的に切り替えるために使われます。
動的にどのコンポーネントを表示するかを切り替えたいときに便利です。
📌 例:
<component :is="modalComponent" />
このように書くと、modalComponent
の値(例えば 'ModalA' や 'ModalB' など)によって表示するコンポーネントを切り替えられます。
modalComponent
にはコンポーネントを入れる変数の役割
Kiroku.vue
<template>
<div>
<!-- 教科ごとの記録モーダルを開くボタン -->
<div class="flex flex-col space-y-2 mt-4">
<button @click="handleOpenModal('modal-japanese')">国語の記録</button>
<button @click="handleOpenModal('modal-math')">数学の記録</button>
<button @click="handleOpenModal('modal-science')">理科の記録</button>
<button @click="handleOpenModal('modal-english')">英語の記録</button>
</div>
<!-- モーダルの表示部分 -->
<div v-if="activeModal" class="modal-content fixed inset-0 z-50">
<div class="min-h-screen flex items-center justify-center">
<div class="fixed inset-0 bg-gray-900/30 transition-opacity"></div>
<div class="flex-1 overflow-y-auto p-5">
<component
:is="modalComponent"
:modalId="activeModal"
:studentId="selectedStudentStore.current.student_id"
:studentName="selectedStudentStore.current.student_name"
:recordDate="selectedStudentStore.current.date"
:subject="currentSubject"
:isOpen="true"
@close="closeModal"
@confirm="handleModalSave"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref, computed } from 'vue';
import JapaneseModal from '../Modals/JapaneseModal.vue';
import MathModal from '../Modals/MathModal.vue';
import ScienceModal from '../Modals/ScienceModal.vue';
import EnglishModal from '../Modals/EnglishModal.vue';
import { useSelectedStudentStore } from '../stores/selectedStudent';
export default {
name: 'SubjectRecordPage',
components: {
JapaneseModal,
MathModal,
ScienceModal,
EnglishModal,
},
setup() {
const activeModal = ref(null);
const currentSubject = ref('');
const selectedStudentStore = useSelectedStudentStore();
const handleOpenModal = (modalType) => {
activeModal.value = modalType;
// 教科名をセット(表示や保存に使える)
currentSubject.value = {
'modal-japanese': '国語',
'modal-math': '数学',
'modal-science': '理科',
'modal-english': '英語',
}[modalType] || '';
};
const closeModal = () => {
activeModal.value = null;
};
const handleModalSave = (data) => {
console.log('保存されたデータ:', data);
closeModal();
};
const modalComponent = computed(() => {
switch (activeModal.value) {
case 'modal-japanese':
return JapaneseModal;
case 'modal-math':
return MathModal;
case 'modal-science':
return ScienceModal;
case 'modal-english':
return EnglishModal;
default:
return null;
}
});
return {
activeModal,
currentSubject,
selectedStudentStore,
handleOpenModal,
closeModal,
handleModalSave,
modalComponent,
};
},
};
</script>
モーダルを表示に使用する関数
const handleOpenModal = (modalType) => {
activeModal.value = modalType;
// 教科名をセット(表示や保存に使える)
currentSubject.value = {
'modal-japanese': '国語',
'modal-math': '数学',
'modal-science': '理科',
'modal-english': '英語',
}[modalType] || '';
};
表示するモーダルの例
JapaneseModal.vue
<template>
<div class="bg-white rounded-xl shadow-lg p-6 w-full max-w-md mx-auto">
<h2 class="text-xl font-semibold mb-4">{{ studentName }} さんの国語記録</h2>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">記録日</label>
<input type="date" v-model="recordDate" class="w-full border rounded px-3 py-2" />
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">学習内容</label>
<input type="text" v-model="lessonContent" class="w-full border rounded px-3 py-2" />
</div>
<div class="mb-4">
<label class="block text-sm font-medium mb-1">理解度</label>
<select v-model="understanding" class="w-full border rounded px-3 py-2">
<option value="">選択してください</option>
<option value="よく理解している">よく理解している</option>
<option value="普通">普通</option>
<option value="理解が不十分">理解が不十分</option>
</select>
</div>
<div class="flex justify-end space-x-2 mt-6">
<button @click="$emit('close')" class="px-4 py-2 bg-gray-300 rounded">キャンセル</button>
<button @click="submit" class="px-4 py-2 bg-blue-600 text-white rounded">保存</button>
</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
// Props
const props = defineProps({
modalId: String,
studentId: String,
studentName: String,
recordDate: String,
subject: String,
isOpen: Boolean,
});
// Emits
const emit = defineEmits(['close', 'confirm']);
// ローカルデータ
const lessonContent = ref('');
const understanding = ref('');
const recordDate = ref(props.recordDate);
// 保存処理
const submit = () => {
emit('confirm', {
studentId: props.studentId,
subject: props.subject,
recordDate: recordDate.value,
lessonContent: lessonContent.value,
understanding: understanding.value,
});
};
</script>
📦 このモーダルができること
生徒名と日付を表示・編集
学習内容(テキスト入力)
理解度(プルダウン)
キャンセルと保存ボタンを提供
保存時に親にデータを返す(@confirm)
✏️ ほかの教科にも応用できます
MathModal.vue → 数式や問題数の入力欄に変更
ScienceModal.vue → 実験内容・観察記録など
EnglishModal.vue → 英単語・スピーキング評価など