Edited at

Vue.jsの勉強で超簡単なタスク管理アプリを作ってみる

More than 1 year has passed since last update.


はじめに

現在、個人的にVue.jsを勉強しております。

個人ブログの方に、経過はまとめております。

(まだ基本文法くらいですが。。。)

Vue.js入門その1〜基本文法〜

Vue.js入門その2〜Vueインスタンスってなんぞ?〜

ただ、私がJSフレームワークの経験がないためだと思いますが、いまひとつピンとこなかったので、よくあるTODOアプリを作成してみました。

同様に勉強している方の参考になれば嬉しく思います。

サンプルはこちらです。(7/12 リンクを差し替えました

https://jsfiddle.net/naoki85/fo26rmr0/12/

JS Fiddleを使用しているので、Vue.jsのバージョンは 2.2.1 になります。

スクリーンショット 2017-07-06 8.39.27.png


準備


External ResourcesにてBootstrapを読み込み

CSSは面倒なので、Bootstrapを使用したいと思います。

CDNのURLを取得し、JS Fiddleの左側、External Resourcesに入力しておきます。

このとき、BootstrapのJSはJQueryを必要とするのでCSSのみにしておきます。

(後でコンソールでエラーが出てしまうので。。。)


目標のかたちをモック作成

<table class="table table-striped">

<thead>
<tr>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Studying JavaScript</td>
<td>
<div class="btn btn-danger">Done!</div>
</td>
</tr>
<tr>
<td>Studying PHP</td>
<td>
<div class="btn btn-danger">Done!</div>
</td>
</tr>
<tr>
<td>Studying Ruby</td>
<td>
<div class="btn btn-danger">Done!</div>
</td>
</tr>
<tr>
<td><input class="form-control"></td>
<td>
<div class="btn btn-default">Create Task</div>
</td>
</tr>
</tbody>
</table>


タスクの一覧表示


Vueインスタンスの作成

var vm = new Vue({

el: '#tasks-index',
data: {
tasks: [
{ id: 1, name: 'Studying JavaScript' },
{ id: 2, name: 'Studying PHP' },
{ id: 3, name: 'Studying Ruby' },
]
}
})


v-forを使用してループ処理

HTMLのテーブルタグの方にid="tasks-index"を割り当てておきます。

また、v-forを使用してtasksの値をループさせて出力します。

v-for

+ <table id="tasks-index" class="table table-striped">

<thead>
<tr>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
+ <tr v-for="task in tasks">
+ <td>{{ task.name }}</td>
<td>
<div class="btn btn-danger">Done!</div>
</td>
</tr>
<tr>
<td><input class="form-control"></td>
<td>
<div class="btn btn-default">Create Task</div>
</td>
</tr>
</tbody>
</table>

これで、一覧表示はできましたので、次は新規登録です!


タスクの新規登録


クリックイベントの設定

「Create Task」ボタンが押されたら、tasksプロパティに値を追加したいので、クリックイベントを設定します。

v-on:clickを使用すれば、設定したインスタンスメソッドにとばすことができます。

v-on

イベントハンドリング

今回は後ほど、createTaskというメソッドを作りたいと思います。

    <tr>

+ <td><input v-model="newTask" class="form-control"></td>
<td>
+ <div class="btn btn-default" v-on:click="createTask">Create Task</div>
</td>
</tr>
</tbody>
</table>

また、入力フォームとしてinputタグを使用していますが、そこにv-modelnewTaskというプロパティをバインディングしています。

新規登録に際し、新しい値をどこかに保持する必要があると考えたのですが、とりあえず新しいインスタンスプロパティを用意し、そこに保持する方法としました。

(このあたりはもっとスマートな方法がありそうです。。。)

v-model


インスタンスに追記

var vm = new Vue({

el: '#tasks-index',
data: {
tasks: [
{ id: 1, name: 'Studying JavaScript' },
{ id: 2, name: 'Studying PHP' },
{ id: 3, name: 'Studying Ruby' },
],
+ newTask: '',
},
+ methods: {
+ createTask: function (event) {
+ // 新しいIDを、配列長から取得
+ var new_id = this.tasks.length + 1;
+ // 配列に追加
+ this.tasks.push({ name: this.newTask });
+ // プロパティを空に戻す
+ this.newTask = '';
+ }
+ }
})


タスクの完了(配列から削除)

タスクの完了は、要はtasksプロパティからの削除とします。


クリックイベントの設定

+  <div class="btn btn-danger" v-on:click="doneTask(task.id)">Done!</div>


メソッドの追記

  methods: {

createTask: function () {
var new_id = this.tasks.length + 1;
this.tasks.push({ id: new_id, name: this.newTask });
this.newTask = '';
+ },
+ doneTask: function (task_id) {
+ this.$delete(this.tasks, task_id - 1);
+ }
}


新規登録時のid取得方法を修正

7/7 追記

riawiththesamさんにコメントにてご指摘いただきました。

JSON配列長から新IDを取得してしまうと、物理削除した後にIDの不整合が起こってしまうので、以下のように修正しました。

  methods: {

createTask: function () {
- var new_id = this.tasks.length + 1;
+ var new_id = this.tasks[this.tasks.length - 1].id + 1;
this.tasks.push({ id: new_id, name: this.newTask });
this.newTask = '';
},

少しブサイクですが、配列の最後のidの値から計算するようにしました。

(もうちょっと良い書き方がありそうですが。。。)


論理削除に修正

7/9 追記

上記の修正に合わせ、tasksisDeletedをもたせて、タスクの完了を論理削除にしたいと思います。


Vueインスタンスの修正

先にVueインスタンスのcreateTaskメソッドとdoneTaskメソッドを修正していきます。

methods: {

createTask: function () {
var new_id = this.tasks[this.tasks.length - 1].id + 1;
- this.tasks.push({ id: new_id, name: this.newTask });
+ this.tasks.push({ id: new_id, name: this.newTask, isDeleted: false });
this.newTask = '';
},
doneTask: function (task_id) {
- this.$delete(this.tasks, task_id - 1);
+ this.tasks.filter(function (task) {
+ if (task.id === task_id) {
+ return task.isDeleted = true;
+ }
+ })
}
}

doneTaskでJSのfilter関数を使用します。

これでidが一致した場合にその要素のisDeletedを書き換えます。

filter

初期値にもisDeletedを追加しておきます。

data: {

tasks: [
{ id: 1, name: 'Studying JavaScript', isDeleted: false },
{ id: 2, name: 'Studying PHP', isDeleted: false },
{ id: 3, name: 'Studying Ruby', isDeleted: false },
],
}

7/12 追記

私の理解不足でしたが、filterは新しい配列を作成してしまうので、forEachを使用した方が良い、というご指摘をいただきました。

そのため、doneTaskメソッドを下記のように修正しました。

  doneTask: function (task_id) {

- this.tasks.filter(function (task) {
+ this.tasks.forEach(function (task) {
if (task.id === task_id) {
return task.isDeleted = true;
}
})
}


v-forとv-ifで条件ループ

v-if

v-forとv-if

v-forv-ifを合わせて使用することで、条件分岐しつつループレンダリングできます。

今回はisDeletedがtrueのもののみ表示させます。

<!-- 省略 -->

<tbody>
- <tr v-for="task in tasks">
+ <tr v-for="task in tasks" v-if="!task.isDeleted">
<td>{{ task.name }}</td>
<!-- 省略 -->


さいごに

簡単ではありますが、勉強用に作成してみました。

次の目標はRailsに組み込んで、もう少し手の込んだSPAを作りたいと思います!

もろもろミスがあり、申し訳ありません。

7/10 追記

本記事をブログの方でも公開しました。

よろしければご覧ください。

Vue.js入門その3〜簡単にTODOアプリを作ってみたよ〜