サンプルイメージ
前回記事はこちら↓
下記のようなアプリを作ってみたいと思います↓
【操作手順】
一覧画面から編集したいタスクのID列をクリックする。
①一覧画面(進捗が「完了」のタスクの場合)
一覧画面で進捗が完了しているタスクなら、編集画面のチェックボックスはON表示なっている
②編集画面
逆に、一覧画面で進捗が完了しているタスクなら、編集画面のチェックボックスはOFF表示なっている
②編集画面(進捗が「まだ」のタスクの場合)
ディレクトリ構造
前回記事と同じくディレクトリはこちらになります↓
プロジェクト名
├── pages/
│ ├── index.vue
│ └── detail.vue
├── server/
│ └── api/
│ ├── create.ts
│ └── task.ts
└── prisma/
├── migrate
└── schema.prisma
画面側の(detail.vue)のコード
Vueテンプレートの場合、v-model
といった双方向バインディング機能を活用して遷移先のページに値が渡されるようにしています。
<script setup lang="ts">
import {useRoute} from 'vue-router'
// 現在のルート情報を取得
const route = useRoute();
// クエリパラメータからidを取得
const taskId = route.query.id as number; // ここで型アサーションを加える;
const {data:task,error} = await useAsyncData('taskDetail',() => $fetch(`/api/selectTargetItem?id=${taskId}`));
console.log("取得したJSONデータは、" + JSON.stringify(task));
</script>
<template>
<div>
<h1>タスクの詳細ページ</h1>
<!-- データが正常に取得できた場合 -->
<div v-if="task">
<form @submit.prevent="editTask" class="bg-gray-100 border border-black-300 max-w-sm mx-auto px-4 py-4">
<div>
<input type="hidden" v-model="task.id"/>
</div>
<div class="mb-5">
<input type="text" v-model="task.task"
class="bg-gray-50 border border-gray-300 text-lg rounded-sm w-full"/>
</div>
<div class="mb-5">
<input type="checkbox"
v-model="task.completed"
:checked="task.completed === true"/>
</div>
<button type="submit" class="bg-blue-500 text-white font-bold px-4 py-4 rounded-lg">更新する</button>
</form>
</div>
<div class="flex justify-center py-3">
<a href="/"
class="bg-green-500 text-white font-bold px-4 py-4 rounded-lg">
一覧に戻る
</a>
</div>
</div>
</template>
サーバ側のコード
import { PrismaClient } from '@prisma/client'
export default defineEventHandler(async (event) => {
// クエリパラメータからtaskIdを取得
const { id } = getQuery(event)
// idがstring型の場合、数値に変換
const taskId = parseInt(id as string)
if (isNaN(taskId)) {
return { error: '無効なIDです' }
}
const prisma = new PrismaClient()
try {
// Prismaを使用してtaskIdに対応するデータを取得
const task = await prisma.task.findUnique({
where: {
id: taskId,
},
})
if (!task) {
return { error: 'データが見つかりません' }
}
return task
} catch (error) {
return { error: error.message }
} finally {
// Prismaのクライアントを切断
await prisma.$disconnect()
}
})
ChatGPTからのアドバイス
画面側のコードに関するアドバイス
エラーの原因は、タグのvalue属性に対する記述方法にあります。
具体的には、以下のコード部分です:
<input type="text" v-model="task" value=${{task.task}}/>
ここで、value=${{task.task}}と記述していますが、Vueのテンプレートでは、{{ }}は文字列挿入のシンタックスであり、value属性に直接使うことは適切ではありません。さらに、v-modelを使って双方向バインディングを行っているため、value属性は不要です。
解決方法
value属性を削除して、v-modelだけを使うようにします。
taskがオブジェクトである場合(task.taskのような構造)、v-modelは文字列や単純な値に直接バインディングされるべきですが、もしtaskがオブジェクトである場合は、適切なプロパティにバインドする必要があります。
修正後のコードは以下のようになります。
修正後のコード:
<script setup lang="ts">
import { useRoute } from 'vue-router'
// 現在のルート情報を取得
const route = useRoute();
// クエリパラメータからidを取得
const taskId = route.query.id as number; // 型アサーション
const { data: task, error } = await useAsyncData('taskDetail', () => $fetch(`/api/selectTargetItem?id=${taskId}`));
console.log("取得したJSONデータは、" + JSON.stringify(task));
</script>
<template>
<div>
<h1>タスクの詳細ページ</h1>
<h2>タスクのID値は、{{ taskId }}</h2>
<!-- データが正常に取得できた場合 -->
<div v-if="task">
<form @submit.prevent="aa">
<div>
<!-- `task.task` にバインディング -->
<input type="text" v-model="task.task" />
</div>
<div>
<!-- 完了状態を表示・変更できるようにする -->
<input type="checkbox" v-model="task.completed" :checked="task.completed === true" />
</div>
</form>
<p>タスク名: {{ task.task }}</p>
<p>完了: {{ task.completed }}</p>
</div>
</div>
</template>
変更点:
value=${{task.task}}を削除し、v-model="task.task"を使用しています。
チェックボックスには、v-model="task.completed"を使用して、完了状態を双方向バインディングしています。
サーバ側のアドバイス
Nuxt3でサーバー側のTypeScriptでtaskIdを取得する方法について説明します。まず、Vueコンポーネントで$fetchを使ってAPIを呼び出し、taskIdをクエリパラメータとして渡します。その後、サーバー側でこのtaskIdを取得し、データを処理する方法を示します。
- VueコンポーネントからtaskIdを渡す
まず、クライアントサイド(Vueコンポーネント)からAPIを呼び出す際に、taskIdをクエリパラメータとして渡します。
タスクの詳細ページ
タスクID: {{ taskId }}
<div v-if="task">
<p>タスク名: {{ task.title }}</p>
<p>完了: {{ task.completed }}</p>
</div>
<div v-else>
<p>タスクが見つかりません。</p>
</div>
- サーバーサイドでtaskIdを取得する方法
サーバーサイドでtaskIdを取得するには、APIハンドラー内でgetQueryを使ってクエリパラメータからtaskIdを取り出します。Nuxt3のAPIルートは、server/apiディレクトリ内に定義されます。
以下に、taskIdを取得してPrismaを使用するサンプルコードを示します。
import { PrismaClient } from '@prisma/client'
export default defineEventHandler(async (event) => {
// クエリパラメータからtaskIdを取得
const { id } = getQuery(event)
// idがstring型の場合、数値に変換
const taskId = parseInt(id as string)
if (isNaN(taskId)) {
return { error: '無効なIDです' }
}
const prisma = new PrismaClient()
try {
// Prismaを使用してtaskIdに対応するデータを取得
const task = await prisma.task.findUnique({
where: {
id: taskId,
},
})
if (!task) {
return { error: 'データが見つかりません' }
}
return task
} catch (error) {
return { error: error.message }
} finally {
await prisma.$disconnect() // Prismaのクライアントを切断
}
})
3. ポイント
getQuery(event):これにより、HTTPリクエストのクエリパラメータを取得できます。この例では、id(taskId)を取得します。
クエリパラメータとして渡されるidは通常string型なので、parseIntでnumber型に変換します。
prisma.task.findUniqueを使って、取得したtaskIdを基にデータベースからタスクデータを取得します。
4. クエリパラメータの取得方法
クエリパラメータは、URLの?以降に渡されるキーと値のペアです。例えば、/api/selectTargetItem?id=123というURLでid=123を渡します。
getQueryはそのidを取得し、TypeScriptで扱えるようにします。
まとめ
クライアントサイドでtaskIdをクエリパラメータとして渡すために、$fetchを使用します。
サーバーサイド(server/api/selectTargetItem.ts)で、getQuery(event)を使ってtaskIdを取得し、その値を数値に変換してPrismaでデータベースを操作します。
この方法で、Nuxt3のサーバーサイドでtaskIdを取得し、データベースから該当するデータを取得することができます。