このチャプターの目次
TODOページを作る
もしローカルでVueアプリを起動していない場合は起動しておいてください。
$ cd front-app
$ npm run serve
ファイルを生成する
viewsディレクトリ以下にファイルを生成します。
$ cd views
$ touch Todo.vue
生成したTodo.vueに下記の内容をコピペします。
Todo.vue
<template>
<div class="todo">
<h1>TODO APP</h1>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Todo',
});
</script>
Routeを用意する
Todoページを表示するpathを用意します。
Vueはrouter/index.ts内にルートの情報を書いていきます。
下記のように追記します。(コメントは書かないでください)
router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import Home from '../views/Home.vue';
import Todo from '../views/Todo.vue'; // add
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
// add start
{
path: '/todo',
name: 'Todo',
component: Todo,
},
// end
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
リンクを追加する
TODOページへのリンクを設置します。
下記のようにApp.vueに追記します。(コメントは書かないでください)
App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> | // add ('|')
<router-link to="/todo">Todo</router-link> // add
</div>
<router-view />
</template>
...
そうすると画面上部にリンクが設置され、クリックするとTODOページに遷移します。
TODOを作成してDBに保存する
ここからは実際にローカル環境に立ち上がっているExpressに対してリクエストを投げてTODOタスクを作成したり取得したりしていきます。
ローカルサーバ環境を立ち上げていない場合は立ち上げておいてください。
$ cd server
$ npm run dev
Vueからローカルサーバにリクエストをするためにaxiosを使うのでインストールしておきます。
$ cd front-app
$ npm i axios
作成するためのフォームを作成する
Todo.vueの内容を下記のように書き換えます。
Todo.vue
<template>
<div class="todo">
<h1>TODO APP</h1>
<div class="create">
<div class="title">
<label>title:</label>
<input v-model="state.title" />
<button @click="createTodo">Create</button>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import axios from 'axios';
const baseURL = 'http://localhost:3000/';
export default defineComponent({
name: 'Todo',
setup() {
const state = reactive({
title: '',
});
const createTodo = async () => {
await axios.put(baseURL, { title: state.title });
};
return {
state,
create,
};
},
});
</script>
このときの画面の見た目はこんな感じになっているはずです。
inputにタイトルを入力してCreateボタンをクリックするとTODOタスクが作成されてDBに保存されます。
TODOを取得して表示する
TODOをDBから取得してページに表示します。
Todo.vueに下記の内容を追記します。(コメントは書かないでください)
Todo.vue
<template>
<div class="todo">
<h1>TODO APP</h1>
<div class="create">
<div class="title">
<label>title:</label>
<input v-model="state.title" />
<button @click="createTodo">Create</button>
</div>
</div>
// add start
<div v-for="todo in state.todos" :key="todo.uuid">
<div class="title margin-r1">title: {{ todo.title }}</div>
<div class="status margin-r1">status: {{ todo.status }}</div>
</div>
// end
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import axios from 'axios';
const baseURL = 'http://localhost:3000/';
export default defineComponent({
name: 'Todo',
setup() {
const state = reactive({
title: '',
todos: null,
});
// add start
const getTodos = () => {
axios.get(baseURL).then((res) => {
if (res && res.data) {
console.log(res);
state.todos = res.data.resBody;
}
});
};
getTodos();
// end
const createTodo = async () => {
await axios.put(baseURL, { title: state.title });
getTodos(); // add
};
return {
state,
create,
};
},
});
</script>
// add start
<style lang="scss" scoped>
.todo-item {
display: flex;
justify-content: center;
.margin-r1 {
margin-right: 1rem;
}
}
</style>
// end
TODOを編集/削除する
TODOを編集したり、削除したりするためにTodo.vueを下記のように編集します。(コメントは書かないでください)
Todo.vue
<template>
<div class="todo">
<h1>TODO APP</h1>
<div class="create">
<div class="title">
<label>title:</label>
<input v-model="state.title" />
<button @click="createTodo">Create</button>
</div>
</div>
<div class="todo-item" v-for="todo in state.todos" :key="todo.uuid">
// add start
<!-- <div class="title margin-r1">title: {{ todo.title }}</div> -->
<input class="margin-r1" v-model="todo.title" />
<!-- <div class="status margin-r1">status: {{ todo.status }}</div> -->
<select class="margin-r1" v-model="todo.status">
<option value="todo">TODO</option>
<option value="wip">WIP</option>
<option value="done">DONE</option>
</select>
<button class="update margin-r1" @click="updateTodo(todo)">Update</button>
<button class="delete margin-r1" @click="deleteTodo(todo)">Delete</button>
// end
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import axios from 'axios';
const baseURL = 'http://localhost:3000/';
type Todo = {
uuid: string;
title: string;
status: 'todo' | 'wip' | 'done';
};
export default defineComponent({
name: 'Todo',
setup() {
const state = reactive({
title: '',
todos: [],
});
const getTodos = () => {
axios.get(baseURL).then((res) => {
if (res && res.data) {
console.log(res);
state.todos = res.data.resBody;
}
});
};
getTodos();
const createTodo = async () => {
await axios.put(baseURL, { title: state.title });
getTodos();
};
// add start
const updateTodo = async (todo: Todo) => {
await axios.post(baseURL + todo.uuid, {
title: todo.title,
status: todo.status,
});
getTodos();
};
const deleteTodo = async (todo: Todo) => {
await axios.delete(baseURL + todo.uuid);
getTodos();
};
// end
return {
state,
createTodo,
updateTodo, // add
deleteTodo, // add
};
},
});
</script>
<style lang="scss" scoped>
.todo-item {
display: flex;
justify-content: center;
margin: 10px 0;
.margin-r1 {
margin-right: 1rem;
}
}
</style>
ここまで書くとこのような動きができると思います。
これでTODOページが完成しました!