Vue.js TODO管理アプリを作成する(その2:いったんの完成)
前回、TODO管理アプリはTODO一覧を表示するところまで出来ていた。それを改造して、新規作成と編集ができるようにする。
新規作成画面
新規作成画面と確認画面を用意する。新規作成画面はフォームを2つ用意して入力させる。OKボタンを押すと、確認画面へ遷移する。
ここでは、新規作成画面で入力した値を確認画面に渡すところで苦戦した。以下の3つの方法を思いついた。
- コンポーネント間通信でデータを受け渡し
- 親コンポーネントに新規TODOのデータを持たせる
- storeオブジェクトに保存
コンポーネント間通信は、画面遷移すると前の画面のコンポーネントが消えるようで、うまく動かなかった。2つのコンポーネントが同じ画面に表示されている状態ならば、うまく通信ができる。次の親コンポーネントにデータを持たせる方法は、親コンポーネントが肥大化してしまう。ということで、最後のstoreオブジェクトを使う方法に落ち着いた。
var store = {
todo: {
title: '',
detail: ''
},
setTodo (title, detail) {
this.todo.title = title;
this.todo.detail = detail;
},
clearTodo () {
this.todo.title = ''
this.todo.detail = ''
}
}
このstoreオブジェクトはデータの入れ物として使うだけでなく、状態管理のためにも使われる。将来的にはVuexなども取り入れたい。
参考:
コンポーネント 親子間以外の通信 - Vue.js
状態管理 - Vue.js
Vuex Introduction · GitBook
編集ボタン
編集ボタンのリンクは、パラメータをつけられるようにした。"/edit/1"といったURLとなる。routerに設定し、パラメータを渡せるリンクを用意する。
const routes = [
{ path: '/', component: Todos },
{ path: '/new', component: New },
{ path: '/newConfirm', component: Confirm },
{ path: '/edit/:item', name:'edit', component: Edit }
]
<router-link :to="{ name:'edit', params:{item:index} }">edit</router-link>
このパラメータは、$routerから取り出すことができる。
var index = this.$router.currentRoute.params.item;
いったんの完成
SPAで動くTODO管理アプリが完成した。TODOの新規追加と編集ができる。下に、現時点のソースコードを載せている。
完成したと言っても、まだまだ改善の余地がある。次回は、単一ファイルコンポーネントに挑戦しようと思う。
ここまでのソースコード
<!DOCTYPE html>
<html>
<head>
<title>TODO Sample</title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<!-- HTMLを書く -->
<div id="app">
<h1>初めてのTODOアプリ</h1>
<p>
<router-link to="/">Go to Top</router-link>
</p>
<router-view
v-bind:todos="todos"
v-on:add-new-todo='addNewTodo'
v-on:edit-new-todo='editNewTodo'
></router-view>
</div>
<!-- トップページのテンプレート -->
<script type="text/x-template" id="todo-list-template">
<div>
<p>TODO一覧</p>
<template v-for="(item, index) in todos" >
<span>
{{ item }}
<router-link :to="{ name:'edit', params:{item:index} }">edit</router-link><br/>
</span>
</template>
<input type="button" value="新規作成" v-on:click="goNewTask()">
</div>
</script>
<!-- 新規作成画面のテンプレート -->
<script type="text/x-template" id="todo-new-template">
<div>
<p>TODO新規作成</p>
タスク名<br>
<input type="text" name="title" size="30" maxlength="20" v-model="title"><br><br>
詳細<br>
<input type="text" name="detail" size="45" maxlength="40" v-model="detail"><br><br>
<input type="button" value="作成する" v-on:click="buttonClick()">
</div>
</script>
<!-- 新規作成確認画面のテンプレート -->
<script type="text/x-template" id="todo-new-confirm-template">
<div>
<p>
タスク名:<span>{{ this.title }}</span><br>
詳細:<span>{{ this.detail }}</span><br>
<p>
<p>本当にこのタスクを作成しますか?<br>
<input type="button" value="OK" v-on:click="okButtonClick()">
<input type="button" value="戻る" v-on:click="cancelButtonClick()">
</p>
</div>
</script>
<!-- 編集画面のテンプレート -->
<script type="text/x-template" id="todo-edit-template">
<div>
<p>編集画面</p>
タスク名<br>
<input type="text" name="title" size="30" maxlength="20" v-model="title"><br><br>
<input type="button" value="編集完了する" v-on:click="okButtonClick()">
</div>
</script>
<script type="text/javascript">
var store = {
todo: {
title: '',
detail: ''
},
setTodo (title, detail) {
this.todo.title = title;
this.todo.detail = detail;
},
clearTodo () {
this.todo.title = ''
this.todo.detail = ''
}
}
// トップページのコンポーネント
const Todos = {
template: '#todo-list-template',
props: ['todos'],
data: function (){
return { }
},
methods: {
goNewTask: function() {
store.clearTodo();
router.push('/new');
}
},
created: function () {
}
}
// 新規作成画面のコンポーネント
const New = {
template: '#todo-new-template',
data: function (){
return {
title: "", detail: ""
}
},
methods: {
buttonClick: function() {
console.log("new button click");
console.log("new todo is " + this.title + "," + this.detail);
store.setTodo(this.title, this.detail);
router.push('/newConfirm');
}
},
created: function () {
this.title = store.todo.title;
this.detail = store.todo.detail;
}
}
// 確認画面のコンポーネント
const Confirm = {
template: '#todo-new-confirm-template',
data: function (){
return {
title: "", detail: ""
}
},
methods: {
okButtonClick: function() {
console.log("ok button click");
this.$emit('add-new-todo', this.title, this.detail);
router.push('/');
},
cancelButtonClick: function() {
console.log("cancel button click");
router.go(-1); // 1つ戻る
}
},
created: function () {
this.title = store.todo.title;
this.detail = store.todo.detail;
}
}
// 編集画面のコンポーネント
const Edit = {
template: '#todo-edit-template',
props: ['todos'],
data: function (){
var index = this.$router.currentRoute.params.item;
return {
title: this.todos[index]
}
},
methods: {
okButtonClick: function() {
console.log("ok button click");
var index = this.$router.currentRoute.params.item;
this.$emit('edit-new-todo', index, this.title);
router.push('/');
}
},
created: function() {
}
}
const routes = [
{ path: '/', component: Todos },
{ path: '/new', component: New },
{ path: '/newConfirm', component: Confirm },
{ path: '/edit/:item', name:'edit', component: Edit }
]
const router = new VueRouter({
routes // routes: routes の短縮表記
})
const app = new Vue({
router,
message: "message",
data: {
todos: [
"todo1",
"todo2",
"todo3"
]
},
methods:{
addNewTodo: function(t, d) {
console.log("addNewTodo "+t+":"+d);
this.todos.push(t);
},
editNewTodo: function(index, title) {
console.log("editNewTodo "+index+":"+title);
this.todos[index] = title;
}
}
}).$mount('#app')
</script>
</body>
投稿日 2017年05月28日