0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Nuxt3】ちょっとしたタスク管理アプリを作ってみる

Posted at

サンプルイメージ

作りかけですが、備忘録としてNuxt3を使ったアプリを作ってみます。
どういったものかといいますと、下記の画面から登録するタスク名を入力します。
inputタグのすぐ下にチェックボックス(TrueまたはFalse)をチェックを入れるとデータベースPostgreSQLに登録されます。
①タスク一覧画面
vue1.png

また、前述の位置ら㎜画面のID列のボタンをクリックすると、こちら②の詳細画面に遷移して陸得るとパラメータからID値を画面に表示するというものです。

②詳細画面(作りかけ)
vue2.png

使用する技術スタック

ソフトウェア
Nuxt3
TypeScriot
TailwindCSS
Prisma
PostgreSQL

ディレクトリ構造

プロジェクト名
├── pages/
│   ├── index.vue
│   └── detail.vue
├── server/
│   └── api/
│       ├── create.ts
│       └── task.ts
└── prisma/
    ├── migrate
    └── schema.prisma

アプリ作成前の準備

今回、Nuxt3とデータベースを接続するために使うORMマッパーは、Prismaを使います。
こちらをプロジェクト直下のコマンド画面でインストールしておきましょう。

タスクの情報をデータベースに入れるので、PostgreSQLの接続情報を入れましょう。

.env
DATABASE_URL="postgresql://(ユーザー名):(パスワード)@localhost:5432/(データベース名)"

今回使用するために更新したデータベース情報はこちら↓

.env
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/nuxtdb"

スキーマ定義を作成する

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Task {
  id         Int      @id @default(autoincrement())
  task       String
  completed  Boolean
  created_at DateTime @default(now())
}

(フロント側)一覧画面の作成

さて、準備が整ったのでまずは一覧画面を作ってみます。

タスク名が空文字を入力した場合は、登録しない処理も追加しておきました。

pages/index.vue
  // 空のタスクは送信しない
  if (!task.value) {
    //警告メッセージを表示する
    taskErrorWarning.value = 'タスク名を入力してください。';
    return
  }; 

  //以降の処理は省略

全体のコードはこちら↓

pages/index.vue
<script setup lang="ts">
import { ref } from 'vue';

// 新しいタスクを保持するローカル変数
const task = ref('');
//エラーメッセージをref定義する
const taskErrorWarning = ref('');
//チェックボックスをref定義する
const checked = ref(false);

// 全タスクを取得する
const { data: tasks } = useFetch('/api/task');

const handleCreateData = async (e: Event) => {
  // フォーム送信によるページリロードを防ぐ
  e.preventDefault(); 

  //警告メッセージを削除する
  taskErrorWarning.value = '';
  
  // 空のタスクは送信しない
  if (!task.value) {
    //警告メッセージを表示する
    taskErrorWarning.value = 'タスク名を入力してください。';
    return
  }; 

  const newTask = await useFetch('/api/create', {
    method: 'POST',
    body: {
      task: task.value,
      checked:checked.value,
    },
  });

  if(!newTask){
    //データベース登録失敗の場合
    let failValue = document.getElementById('failMag');
    failValue.innerHTML= '登録に失敗しました。';
    return;
  }

  let successValue = document.getElementById('successMag');
  successValue.innerHTML = '登録に成功しました。';
  console.log(task.value);
};
</script>

<template>
  <div class="flex items-center justify-center min-h-screen bg-gray-100">
    <div>
      <span id="successMag" class="h-14"></span>
      <span id="failMag" class="h-14"></span>
    </div>
    <div class="w-full max-w-sm p-6 bg-white rounded-lg shadow-md">
        <h1>タスク登録</h1>
        <form @submit.prevent="handleCreateData">
          <div>
            <input v-model="task" class="shadow apperrance-none border rounded" placeholder="タスクを入力" maxlength="10"/>
            <div class="inpurWarningArea">
              <span v-if="taskErrorWarning" class="label text-warning text-red-500">{{taskErrorWarning}}</span>
            </div>        
          </div>
          <div>
            <input type="checkbox" v-model="checked"/>
          </div>
          <div class="flex items-center justify-between">
            <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
              タスクを登録
            </button>
          </div>
      </form>
    </div>   
    <table class="table-auto">
      <thead>
        <tr>
          <th scope="col" class="border border-black px-4 py-4">ID</th>
          <th scope="col" class="border border-black px-4 py-4">タスク</th>
          <th scope="col" class="border border-black px-4 py-4">進捗</th>
          <th scope="col" class="border border-black px-4 py-4">更新日時</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="task in tasks" :key="task.id">
          <td class="border border-black px-4 py-4">
            <a :href="`/detail?id=${task.id}`" class="bg-green-500 text-white font-bold py-2 px-4 rounded">{{task.id}}</a>
          </td>
          <td class="border border-black px-4 py-4">{{task.task}}</td>
          <td class="border border-black px-4 py-4">
            <span v-if="task.completed == true" class="bg-blue-500 text-white font-bold py-2 px-4 rounded">完了</span>
            <span v-if="task.completed == false" class="bg-red-500 text-white font-bold py-2 px-4 rounded">まだ</span>
          </td>
          <td class="border border-black px-4 py-4">{{task.created_at}}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

つぎに、詳細画面です(作りかけです...)
一覧画面のID列をクリックしてリクエストパラメータにIDを付与します。
そのID値をvue-routerコンポーネントで受け取って画面に表示します。
Nuxt3では、vue-routerコンポーネントは標準でついているのでパッケージ管理ツールyarnnpmでのインストールは不要です。

pages.detail.vue
<script setup lang="ts">
import {useRoute} from 'vue-router'
// 現在のルート情報を取得
const route = useRoute();

// クエリパラメータからidを取得
const taskId = route.query.id;

</script>
<template>
    <div>
        <h1>タスクの詳細ページ</h1>
        <h2>タスクのID値は、{{taskId}}</h2>    
    </div>
    
    
</template>

サーバ側の作成

データベース登録するためのコード↓

server/api/create.ts
import { PrismaClient  } from "@prisma/client"

const prisma = new PrismaClient()

export default defineEventHandler(async (event) => {
  const body = await readBody(event);

  //データベース登録完了フラグを用意する
  let flag:boolean = false;
  
  try{
    const newTask = await prisma.task.create({
      data: {
        task: body.task,
        completed: body.checked,//boolean値
      }
    });
    flag = true;
    return flag;
  }catch(error){
    return flag;
  }
  
})

データベースからレコードを取得するためのコード↓

server/api/task.ts
import { PrismaClient } from "@prisma/client";

export default defineEventHandler(async(event)=>{
    const prisma = new PrismaClient();
    if(event.node.req.method === 'GET'){
        const tasks = await prisma.task.findMany();
        return tasks;
    }
    if(event.node.req.method === 'POST'){
        const body = await readBody(event);
        const newTask = await prisma.task.findMany({
            select:{
                id:true,
                task:true,
                completed:true
            },
            orderBy:{
                id:'asc'
            }
        })
              
        console.log("セレクトしたタスクは、" + newTask);
        return newTask;
    }
});

以上です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?