LoginSignup
36

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-07-06

はじめに

現在、個人的に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アプリを作ってみたよ〜

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
What you can do with signing up
36