呼び出し側のコード
TaskView.vue
<template>
<div class="task-view">
<div class="task-header">
<h2 class="text-2xl font-bold mb-4">タスク詳細</h2>
<div class="flex space-x-4">
<button
@click="showEditModal = true"
>
編集
</button>
</div>
</div>
<!-- 編集モーダル -->
<TaskModal
v-if="showEditModal"
:show="showEditModal"
:editing-task="task"
:categories="categories"
@submit="handleTaskSubmit"
@cancel="showEditModal = false"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { db } from '../firebase_settings/index.js';
import { doc, getDoc, updateDoc, deleteDoc, collection, getDocs } from 'firebase/firestore';
import TaskModal from '../Modals/TaskModal.vue';
const route = useRoute();
const router = useRouter();
const task = ref(null);
const categories = ref([]);
const showEditModal = ref(false);//タスクモーダルの表示の有無のフラグ
// タスクモーダルから emit('submit') が実行されたときに実行する関数
const handleTaskSubmit = async (taskData) => {//taskDataはタスクモーダルからの引数
try {
const taskRef = doc(db, 'tasks', task.value.id);
await updateDoc(taskRef, {
...taskData,
categoryId: taskData.categoryId || taskData.projectId, // 既存のcategoryIdを保持
updated_at: new Date()
});
await fetchTask();
showEditModal.value = false;
} catch (error) {
console.error('タスクの更新に失敗しました:', error);
}
};
</script>
ポイント
「:show="変数名"」はモーダル側に渡す変数
「@submit="handleTaskSubmit"」は モーダル側で「emit('submit', taskData);」が実行されたときに実行する関数名
「@cancel="showEditModal = false"」は モーダル側で「emit('cancel')」が実行された時に「showEditModal」変数に false を渡してモーダルを非表示にする処理が実行されるという意味
<!-- 編集モーダル -->
<TaskModal
v-if="showEditModal"
:show="showEditModal"
:editing-task="task"
:categories="categories"
@submit="handleTaskSubmit"
@cancel="showEditModal = false"
/>
モーダル画面のコード
TaskModal.vue
<template>
<div v-if="show" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-[70]">
<div class="relative mx-auto p-5 border shadow-lg rounded-md bg-white modal-container">
<div class="mt-3">
<h3 class="text-lg font-medium leading-6 text-gray-900 mb-4">
{{ editingTask ? 'タスクの編集' : '新規タスクの追加' }}
</h3>
<form @submit.prevent="handleSubmit">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="title">
タイトル
</label>
<input
id="title"
v-model="formData.title"
type="text"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
required
/>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="content">
内容
</label>
<textarea
id="content"
v-model="formData.content"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
rows="4"
required
></textarea>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="category">
カテゴリ
</label>
<select
id="category"
v-model="formData.projectId"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
>
<option value="">カテゴリを選択</option>
<option
v-for="category in categories"
:key="category.id"
:value="category.id"
>
{{ category.title }}
</option>
</select>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="priority">
優先度
</label>
<select
id="priority"
v-model="formData.priority"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
>
<option value="0">低</option>
<option value="1">中</option>
<option value="2">高</option>
<option value="3">緊急</option>
</select>
</div>
<div class="flex justify-end space-x-2">
<button
type="button"
@click="$emit('cancel')"
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
>
キャンセル
</button>
<button
type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
>
{{ editingTask ? '更新' : '追加' }}
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { getFirestore, collection, doc, setDoc } from 'firebase/firestore';
import { v4 as uuidv4 } from 'uuid';
const props = defineProps({
show: {
type: Boolean,
required: true
},
editingTask: {
type: Object,
default: null
},
categories: {
type: Array,
required: true,
default: () => []
},
settings: {
type: Object,
required: true
}
});
const emit = defineEmits(['submit', 'cancel']);
const formData = ref({
title: '',
content: '',
projectId: '',
priority: 0
});
// 編集モードの場合、フォームに既存のデータを設定
watch(() => props.editingTask, (newTask) => {
if (newTask) {
formData.value = {
title: newTask.title,
content: newTask.content,
projectId: newTask.projectId,
priority: newTask.priority
};
} else {
// 新規作成時はフォームをリセットし、デフォルトカテゴリを設定
formData.value = {
title: '',
content: '',
projectId: props.settings.active_category_id?.categoryId || '',
priority: 0
};
}
}, { immediate: true });
const handleSubmit = async () => {
try {
// ドキュメントIDを生成
const docId = uuidv4();
// Firestoreに直接保存
const db = getFirestore();
const taskRef = doc(db, 'tasks', docId);
const taskData = {
id: docId, // ドキュメントIDをフィールドにも追加
title: formData.value.title,
content: formData.value.content,
projectId: formData.value.projectId,
priority: parseInt(formData.value.priority),
type: 'task',
categoryId: formData.value.projectId,
created_at: new Date(),
updated_at: new Date(),
checked: false // デフォルトで未完了状態
};
await setDoc(taskRef, taskData);
// 親コンポーネントに通知
emit('submit', taskData);
} catch (error) {
console.error('タスクの保存に失敗しました:', error);
// エラーハンドリングを追加する場合はここに実装
}
};
</script>
ポイント
propsで親のコンポーネントから値を受け取る
emit()関数で親側に通知ができ、引数を渡すこともできる
const props = defineProps({
show: {
type: Boolean,
required: true
},
editingTask: {
type: Object,
default: null
},
categories: {
type: Array,
required: true,
default: () => []
},
settings: {
type: Object,
required: true
}
});