親子コンポーネントのデータ連携とイベント処理
Vue.jsアプリケーションでは、親コンポーネントがテーブルを表示し、子コンポーネントがモーダルダイアログとして動作する構成が一般的です。この設計では、コンポーネント間のデータ同期が重要になります。
親コンポーネントの実装例
<template>
<div>
<button @click="openDialog('create')">新規追加</button>
<table>
<tr v-for="item in items" :key="item.id">
<td>{{ item.date }}</td>
<td>{{ item.name }}</td>
<td>
<button @click="openDialog('update', item)">編集</button>
<button @click="removeItem(item)">削除</button>
</td>
</tr>
</table>
<FormModal
:visible="modalConfig.visible"
:title="modalConfig.title"
:initialData="modalConfig.data"
@submit="handleFormSubmit"
@close="closeDialog"
/>
</div>
</template>
<script>
import { reactive } from 'vue';
import FormModal from './FormModal.vue';
export default {
components: { FormModal },
setup() {
const state = reactive({
items: [
{ id: 1, date: '2023-01-01', name: 'サンプル1' },
{ id: 2, date: '2023-01-02', name: 'サンプル2' }
],
modalConfig: {
visible: false,
title: '',
data: {}
}
});
function openDialog(mode, item = null) {
state.modalConfig.visible = true;
state.modalConfig.title = mode === 'create' ? '新規追加' : '編集';
state.modalConfig.data = item ? { ...item } : {};
}
function closeDialog() {
state.modalConfig.visible = false;
}
function handleFormSubmit(formData) {
// フォームデータ処理ロジック
}
function removeItem(target) {
// 削除確認ダイアログ表示
}
return {
...state,
openDialog,
closeDialog,
handleFormSubmit,
removeItem
};
}
};
</script>
子コンポーネントの実装例
<template>
<dialog :open="isVisible">
<h2>{{ title }}</h2>
<form @submit.prevent="submitForm">
<label>名称:
<input v-model="formState.name" required>
</label>
<label>日付:
<input v-model="formState.date" type="date" required>
</label>
<button type="submit">確定</button>
<button type="button" @click="cancel">キャンセル</button>
</form>
</dialog>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
props: {
visible: Boolean,
title: String,
initialData: Object
},
emits: ['submit', 'close', 'update:visible'],
setup(props, { emit }) {
const formState = reactive({
name: '',
date: ''
});
watch(() => props.visible, (isOpen) => {
if (isOpen) resetForm();
});
watch(() => props.initialData, (newData) => {
Object.assign(formState, newData || {});
}, { immediate: true });
function resetForm() {
Object.keys(formState).forEach(key => {
formState[key] = '';
});
}
function submitForm() {
emit('submit', { ...formState });
}
function cancel() {
emit('update:visible', false);
emit('close');
}
return {
formState,
submitForm,
cancel
};
}
};
</script>
条件付きバリデーションの実装
追加操作時のみフォームバリデーションを適用する方法:
<script>
import { ref } from 'vue';
export default {
setup() {
const formRef = ref(null);
const operationType = ref('');
function openDialog(mode) {
operationType.value = mode;
if (formRef.value) {
formRef.value.resetFields();
formRef.value.clearValidate();
}
}
async function submitForm() {
if (operationType.value === 'create') {
const isValid = await formRef.value.validate();
if (!isValid) return;
}
// フォーム送信処理
}
return {
formRef,
openDialog,
submitForm
};
}
};
</script>
編集中の行のハイライト表示
<template>
<table>
<tr
v-for="item in items"
:key="item.id"
:class="{ 'editing': item.editing }"
>
<!-- テーブル内容 -->
</tr>
</table>
</template>
<script>
export default {
methods: {
openDialog(mode, item) {
if (mode === 'update') {
this.items.forEach(i => i.editing = false);
item.editing = true;
}
},
closeDialog() {
this.items.forEach(item => item.editing = false);
}
}
};
</script>
<style>
.editing {
background-color: #f0f0f0;
}
</style>
リアクティブなデータ同期
親コンポーネントの状態変化に応じた子コンポーネントの更新:
<script>
export default {
props: ['selectedId'],
setup(props) {
watch(() => props.selectedId, (newId) => {
if (newId) fetchData(newId);
});
function fetchData(id) {
// API呼び出し
}
}
};
</script>