<Breadcrumb :pageNames="['集会一覧', '集会詳細', '演題一覧', '演題新規登録']" />
// 子コンポーネント
<script lang="ts">
export interface Params {
meetingId: string;
emailSendingHistoryId: string;
mailTemplateId: string;
reviewRequestId: string;
reviewId: string;
historyId: string;
administratorId: string;
}
</script>
<script setup lang="ts">
import { computed, PropType } from 'vue';
import { Link, usePage } from '@inertiajs/vue3';
// 📌 `params` を `usePage().props.params` から取得し、分割代入
const {
meetingId,
emailSendingHistoryId,
mailTemplateId,
reviewRequestId,
reviewId,
historyId,
administratorId
} = usePage().props.params as Params;
// `pageNames`(ページ名の配列)を受け取る
const props = defineProps({
pageNames: {
type: Array as PropType<string[]>,
required: true,
},
});
// 📌 `breadcrumbMap` を外部に定義し、パラメータに `params` の値をセット
const breadcrumbMap: Record<string, { routeName: string; params?: string[] }> = {
集会一覧: { routeName: 'meeting.index' },
集会登録: { routeName: 'meeting.create' },
集会詳細: { routeName: 'meeting.show', params: [meetingId] },
集会編集: { routeName: 'meeting.edit', params: [meetingId] },
演題一覧: { routeName: 'abstract.index', params: [meetingId] },
演題登録: { routeName: 'abstract.bulkRegistration.show', params: [meetingId] },
管理者一覧: { routeName: 'administrator.index' },
管理者詳細: { routeName: 'administrator.show', params: [administratorId] },
管理者登録: { routeName: 'administrator.create' },
メール送信履歴: { routeName: 'email.history.index', params: [meetingId] },
メール送信履歴詳細: { routeName: 'email.history.show', params: [meetingId, emailSendingHistoryId] },
メール管理: { routeName: 'email.emailTemplate.index' },
メールテンプレート詳細: { routeName: 'email.emailTemplate.show', params: [mailTemplateId] },
メールテンプレート編集: { routeName: 'email.emailTemplate.edit', params: [mailTemplateId] },
査読依頼一覧: { routeName: 'reviewRequest.index', params: [meetingId] },
査読依頼詳細: { routeName: 'reviewRequest.show', params: [meetingId, reviewRequestId] },
査読割設定: { routeName: 'reviewRequest.create', params: [meetingId] },
査読再割付: { routeName: 'reviewRequest.recreate', params: [meetingId] },
査読依頼メール送信: { routeName: 'reviewRequest.emailSending.create', params: [meetingId, reviewRequestId] },
査読詳細: { routeName: 'review.show', params: [meetingId, reviewRequestId, reviewId] },
査読編集: { routeName: 'review.edit', params: [meetingId, reviewRequestId, reviewId] },
査読可能領域更新依頼メール送信: { routeName: 'reviewableArea.requestUpdate.create' },
査読可能領域メール送信履歴: { routeName: 'reviewableArea.requestUpdate.history.index' },
査読可能領域メール送信履歴詳細: { routeName: 'reviewableArea.requestUpdate.history.show', params: [historyId] },
集会分析: { routeName: 'meetingAnalysis.index', params: [meetingId] },
マスタ管理: { routeName: 'masters.index' },
キーワード一覧: { routeName: 'masters.keywords.index' },
セッション割: { routeName: 'session.index', params: [meetingId] },
集会担当組織: { routeName: 'masters.meetingOrganizations.index' },
採択結果通知メール送信: { routeName: 'abstract.emailSending.create', params: [meetingId] },
キーワード編集: { routeName: 'masters.keywords.edit' },
演題登録フォーム一覧: { routeName: 'abstractForm.list', params: [meetingId] },
};
// 📌 `breadcrumbs` を `computed` で計算
const breadcrumbs = computed(() => {
return props.pageNames.map((pageName) => {
const routeData = breadcrumbMap[pageName];
return {
label: pageName,
href: routeData
? route(routeData.routeName, routeData.params) // 🔥 params にはすでに値が入っているので、そのまま渡す
: '#',
};
});
});
// 📌 `current`(現在のページ)
const current = computed(() => breadcrumbs.value[breadcrumbs.value.length - 1]);
</script>
<template>
<nav class="breadcrumb">
<ul>
<li v-for="breadcrumb in breadcrumbs" :key="breadcrumb.href">
<template v-if="breadcrumb === current">
{{ breadcrumb.label }}
</template>
<template v-else>
<Link :href="breadcrumb.href">{{ breadcrumb.label }}</Link>
</template>
</li>
</ul>
</nav>
</template>
Vue.js + Inertia.js で動的なパンくずリストを作成する方法
このページでは、Vue.js + Inertia.js を使った動的なパンくずリスト(Breadcrumb)コンポーネント の作り方を解説します。
pageNames を渡すだけで、適切なルート付きのナビゲーションを自動生成 できます。
📌 このパンくずリストでできること
✅ 現在のページに応じたパンくずリストを自動生成!
✅ pageNames を渡すだけで、適切なページ遷移が可能!
✅ Inertia.js の usePage().props.params からページごとの ID を自動取得!
✅ Vue の computed() を活用して、最適なルーティング処理を実装!
🛠 親コンポーネントでの使い方
パンくずリストを表示したいページで、以下のように Breadcrumb コンポーネントを使用します。
<Breadcrumb :pageNames="['集会一覧', '集会詳細', '演題一覧', '演題新規登録']" />
✅ この場合のパンくずリスト
📍 集会一覧 > 集会詳細 > 演題一覧 > 演題新規登録
• クリック可能なページ は <Link> で遷移
• 現在のページ はリンクなしで表示
📌 パンくずリストの実装(Vue コンポーネント)
以下が、Vue.js でパンくずリストを動的に生成するコンポーネントです。
<script lang="ts">
export interface Params {
meetingId: string;
emailSendingHistoryId: string;
mailTemplateId: string;
reviewRequestId: string;
reviewId: string;
historyId: string;
administratorId: string;
}
</script>
<script setup lang="ts">
import { computed, PropType } from 'vue';
import { Link, usePage } from '@inertiajs/vue3';
// 📌 `params` を `usePage().props.params` から取得し、分割代入
const {
meetingId,
emailSendingHistoryId,
mailTemplateId,
reviewRequestId,
reviewId,
historyId,
administratorId
} = usePage().props.params as Params;
// `pageNames`(ページ名の配列)を受け取る
const props = defineProps({
pageNames: {
type: Array as PropType<string[]>,
required: true,
},
});
// 📌 `breadcrumbMap` を定義し、各ページに対応するルートを設定
const breadcrumbMap: Record<string, { routeName: string; params?: string[] }> = {
集会一覧: { routeName: 'meeting.index' },
集会登録: { routeName: 'meeting.create' },
集会詳細: { routeName: 'meeting.show', params: [meetingId] },
集会編集: { routeName: 'meeting.edit', params: [meetingId] },
演題一覧: { routeName: 'abstract.index', params: [meetingId] },
演題登録: { routeName: 'abstract.bulkRegistration.show', params: [meetingId] },
管理者一覧: { routeName: 'administrator.index' },
管理者詳細: { routeName: 'administrator.show', params: [administratorId] },
管理者登録: { routeName: 'administrator.create' },
メール送信履歴: { routeName: 'email.history.index', params: [meetingId] },
メール送信履歴詳細: { routeName: 'email.history.show', params: [meetingId, emailSendingHistoryId] },
メール管理: { routeName: 'email.emailTemplate.index' },
メールテンプレート詳細: { routeName: 'email.emailTemplate.show', params: [mailTemplateId] },
メールテンプレート編集: { routeName: 'email.emailTemplate.edit', params: [mailTemplateId] },
査読依頼一覧: { routeName: 'reviewRequest.index', params: [meetingId] },
査読依頼詳細: { routeName: 'reviewRequest.show', params: [meetingId, reviewRequestId] },
査読割設定: { routeName: 'reviewRequest.create', params: [meetingId] },
査読再割付: { routeName: 'reviewRequest.recreate', params: [meetingId] },
査読依頼メール送信: { routeName: 'reviewRequest.emailSending.create', params: [meetingId, reviewRequestId] },
査読詳細: { routeName: 'review.show', params: [meetingId, reviewRequestId, reviewId] },
査読編集: { routeName: 'review.edit', params: [meetingId, reviewRequestId, reviewId] },
キーワード編集: { routeName: 'masters.keywords.edit' },
演題登録フォーム一覧: { routeName: 'abstractForm.list', params: [meetingId] },
};
// 📌 `breadcrumbs` を `computed` で計算
const breadcrumbs = computed(() => {
return props.pageNames.map((pageName) => {
const routeData = breadcrumbMap[pageName];
return {
label: pageName,
href: routeData
? route(routeData.routeName, routeData.params) // 🔥 `params` にすでに値が入っているのでそのまま渡す
: '#',
};
});
});
// 📌 `current`(現在のページ)
const current = computed(() => breadcrumbs.value[breadcrumbs.value.length - 1]);
</script>
<template>
<nav class="breadcrumb">
<ul>
<li v-for="breadcrumb in breadcrumbs" :key="breadcrumb.href">
<template v-if="breadcrumb === current">
{{ breadcrumb.label }}
</template>
<template v-else>
<Link :href="breadcrumb.href">{{ breadcrumb.label }}</Link>
</template>
</li>
</ul>
</nav>
</template>
✅ 修正のポイント
✔ params を usePage().props.params から取得し、分割代入
✔ breadcrumbMap の params に params の値を直接セット
✔ 無駄な map() の処理を削除し、シンプルに params を渡す
✔ route(routeData.routeName, routeData.params) に変更して最適化
🔍 具体的な動作
例1: 査読編集(review.edit) の場合
// `params` の値
const meetingId = '123';
const reviewRequestId = '456';
const reviewId = '789';
// breadcrumbMap のエントリー
査読編集: { routeName: 'review.edit', params: [meetingId, reviewRequestId, reviewId] }
route() に渡る値
route('review.edit', ['123', '456', '789'])
➡ 結果: /review/edit/123/456/789
🚀 まとめ
修正前 | 修正後 |
---|---|
params にキーの文字列を入れていた | params に usePage().props.params の値を直接代入 |
map() を使って params[param] を取得 | params にすでに値が入っているため map() を削除 |
routeData.params?.map(param => params[param]) | route(routeData.routeName, routeData.params) に変更 |
✅ これで params の値を直接使う形に修正完了! 🚀
コメント