Help us understand the problem. What is going on with this article?

Vue.jsミニハンズオン(TODOリスト作成)

Vue.jsミニハンズオン(TODOリスト作成)

AngularでもReactでもriot.jsでも満足できなかったひとに、ぴったりフィットなJSフレームワーク「Vue.js」のざっくりハンズオンです。

このハンズオンではnode.jsのパッケージは使わず、Google ChromeとテキストエディタがあればOKです。

Vue.jsミニハンズオンのシリーズは以下を公開しています。

  1. Vue.jsミニハンズオン(TODOリスト作成)
  2. Vue.jsミニハンズオン(TODOリストにアニメーションをつける)
  3. Vue.jsミニハンズオン(TODOリストをコンポーネント化する)

今回の目標

目標はシンプルなTODOリストの作成です。
WebStorageに保存してリロードしてもデータを保持できるようにしてます。

Chromeの拡張機能を入れる

デベロッパーツールでデータを見やすくするためChromeの拡張機能「Vue.js devtools」を入れます。

Vue.js devtools

Vue.jsを使っているページでデベロッパーツールを表示すると、Elements, Console...の並びにVueが増えているはずです。

CODEPEN

データを表示してみる

データを単純に表示します。Vue.jsのデータ表示の基本はひとまずこれだけ。

  • HTMLの内容にデータを表示するときは {{ データの名前 }} を使用します。
  • JavaScriptで new Vue({ el: '#id属性値', data: { データを設定 } }) でVueインスタンスをつくります。
1.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>まずはデータを表示</title>
</head>
<body>

<div id="app">
  <p>{{ message }}</p>
</div>

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="./app1.js"></script>

</body>
</html>
app1.js
const vm = new Vue({
  el: '#app',
  data: {
    message: 'Hellor Vue.js!',
  }
})

CODEPEN

デベロッパーツールのVueタブをクリックして、<Root>と書いてあるところをクリックすると右側にデータが出てきます。
(上記のCODEPENのリンク先を見ている場合は右上の[Change View]-[Debug Mode]をクリックするとデベロッパーツールを表示したときにVueタブが表示されます。お試しあれ。)

<Root>の横に $vm0 とか表示されているのを確認して(番号が違うかもです)、Consoleタブで $vm0.message と打つとちゃんと値がとれます。
また、vm という変数にVueインスタンスを入れているので vm.message と入れても値がとれます。

データバインディングしてみる

ではテキストボックスに入力したデータとp要素に表示するデータをバインディングしてみましょう。データをバインディングするには対象のHTML要素に v-model というディレクティブを追加します。
ディレクティブはVue.jsが利用する v- という接頭辞が付いた特別な属性を指します。

1.html
<div id="app">
  <input type="text" v-model="message"> <!-- 追加 -->
  <p>{{ message }}</p>
</div>

CODEPEN

これだけです。テキストボックスの中のテキストを編集するとp要素の中のテキストも同時に変わります。デベロッパーツールのVueタブの<Root>をクリックしたときに見えるデータも同時に変わります。

チェックボックスはinput要素に v-model="true" であればチェックがつき、 v-model="false" であればチェックがはずれます。

1.html
<div id="app">
  <input type="text" v-model="message">
  <input type="checkbox" v-model="isChecked"> <!-- 追加 -->
  <p>{{ message }} {{ isChecked }}</p>
</div>
app1.js
const vm = new Vue({
  el: '#app',
  data: {
    message: 'Hellor Vue.js!',
    isChecked: true <!-- 追加 -->
  },
})

CODEPEN

リストを表示してみる

シンプルなtodoリストを作るのが今回の目標なのでリスト表示をしてみます。
リスト表示には対象のHTML要素に v-for というディレクティブを使います。

v-forに指定している itemsdata オプション内にある配列です。
item は配列内のデータの1セットを示す名前です。この名前はどうつけてもOKです。
配列を複数形でつけて、このデータの1セットは単数形にするのはよくある名前の付け方です。

2.html
<div id="app">
  <ul>
    <li v-for="item in items">{{ item.title }}</li>
  </ul>
</div>
app2.js
const vm = new Vue({
  el: '#app',
  data: {
    items: [
        { title: '項目1', id: 1 },
        { title: '項目2', id: 2 },
        { title: '項目3', id: 3 },
    ]
  },
})

CODEPEN

クリックイベントをしかける

クリックイベントで何かしら関数が動くようにしてみます。
イベントは対象のHTML要素に v-on:click ディレクティブを入れます。v-on がイベント、 clickはクリックイベントを示します。値には関数名やJavaScriptの文を入れることができます。

関数はJavaScriptで methods オプションの中に書きます。
dataオプション内のデータは this.データ名 で参照できます。

3.html
<div id="app">
  <p>{{ counter }}</p>
  <button v-on:click="add">追加(関数)</button>
  <button v-on:click="counter++">追加(JavaScriptの文)</button>
</div>
app3.js
const vm = new Vue({
  el: '#app',
  data: {
    counter: 0
  },
  methods: {
    add: function(){
      this.counter++
    },
  },
})

CODEPEN

TODOリストを作成する

おおまかに必要なことは学びました。TODOリストを作っていきましょう。
TODOリストには下記の機能があるとします。

  • TODOの表示
  • TODOの追加
  • TODOのチェック
  • チェックしたTODOの削除

TODOの表示

まず「TODOの表示」をしましょう。label要素とチェックボックスも入れておきます。

todo.html
<div id="app">
  <ul>
    <li v-for="item in items">
      <label>
        <input type="checkbox" v-model="item.isChecked"> {{ item.title }}
      </label>
    </li>
  </ul>
</div>

TODOリストのデータには下記の2つのプロパティを入れておきます。

  • TODOリストの内容
  • チェック済み判定フラグ
todo.js
const vm = new Vue({
  el: '#app',
  data: {
    items: [
        { title: '領収書を準備する', isChecked: true },
        { title: 'Vue.jsハンズオンの資料を作る', isChecked: true },
        { title: '参加者の人数を確認する', isChecked: false },
        { title: 'ピザを注文する', isChecked: false },
        { title: '参加費のお釣りを準備する', isChecked: false },
        { title: '会場設営をする', isChecked: false },
    ]
  },
})

CODEPEN

テキストをクリックしてもチェックされるのがいい感じです。

TODOの追加

TODOの追加は「テキストボックスに文字を入力してEnterキーを押したら、配列 items に新しく項目を追加する」という処理です。

まずはHTMLの <div id="app"> 内に下記を追加します。

todo.html
<p>
  <input type="text"
    placeholder="TODOを入力しましょう!"
    v-model="newItemTitle"
    v-on:keyup.enter="addTodo"></p>

入力した文字は newItemTitle に入ります。
そしてEnterキーを押すと addTodo という関数に渡されます。
ではJavaScriptを書きます。

todo.js
const vm = new Vue({
  el: '#app',
  data: {
    items: [
        { title: '領収書を準備する', isChecked: true },
        { title: 'Vue.jsハンズオンの資料を作る', isChecked: true },
        { title: '参加者の人数を確認する', isChecked: false },
        { title: 'ピザを注文する', isChecked: false },
        { title: '参加費のお釣りを準備する', isChecked: false },
        { title: '会場設営をする', isChecked: false },
    ],
    newItemTitle: '' //追加
  },
  methods: {  //methodsオプションをまるっと追加
    addTodo: function(){
      this.items.push({
        title: this.newItemTitle,
        isChecked: false
      });
    },
  }
})

methods オプションで addTodo 関数を追加しました。配列の push メソッドでデータを追加しています。title はテキストボックスに入力された内容、isChecked は最初からチェックされている項目はないので、false を固定で入れておきます。

ブラウザでテキストボックスで文字を入力してEnterキーを押すと、リストに項目が追加されましたね。

ただし、Enterキーを押したあとに、テキストボックス内にまだ文字が残っているので文字を消しましょう。

todo.js
    addTodo: function(newTitle){
      this.items.push({
        title: newTitle,
        isChecked: false
      });
      this.newItemTitle = ''; //追加
    },

CODEPEN

だいぶアプリっぽくなりましたね。

TODOのチェック

項目がチェックされているときはlabel要素にclass属性を設定して、打ち消し線が入るようにしてみましょう。
class属性を設定するかしないかは

  • v-bind:class="done: true" であれば class="done"
  • v-bind:class="done: false" であれば class=""

となります。チェックをしているかどうかは isChecked で取れるので `v-bind:class="done: isChecked"と設定します。

todo.html
    <li v-for="item in items">
      <label v-bind:class="{ done: item.isChecked }">
        <input type="checkbox" v-model="item.isChecked"> {{ item.title }}
      </label>
    </li>

head要素内に打ち消し線を表示するスタイルを追加しましょう。

todo.html
  <style>
    .done { text-decoration: line-through; }
  </style>

CODEPEN

チェックしたTODOの削除

チェックしたリストがいつまでものこっているのは邪魔っぽいのでまとめて消す機能をつけてみます。

div#app 内に下記の button 要素をを追加します。

todo.html
  <button v-on:click="deleteTodo()">チェック済みの項目を削除する</button>

filter メソッドを使って配列を更新します。

todo.js
    deleteTodo: function(){
      this.items = this.items.filter(function (item) {
        return item.isChecked === false; //
      });
    },

CODEPEN

これでTODOリストの基礎的な表示ができました。

再描画でデータがリセットされるのを防ぐ

再描画するとリストがリセットされてしまうので、データをブラウザに保存し、初期表示時に利用してみます。

データをブラウザに保存する

データをブラウザに保存するときは localStorageitems という配列をまるごと保存します。methodsオプションに下記を追加します。

todo.js
saveTodo: function(){
  localStorage.setItem('items', JSON.stringify(this.items));
},

localStorageに保存するのは localStorage.setItem('フィールド名': 値) で簡単に実装できます。ただし、配列のままでは入れることができず、文字列に変換する必要があるので JSON.stringify で変換します。

ということで項目を追加したときにブラウザに保存するため、関数を呼び出します。

todo.js
    addTodo: function(newTitle){
      this.items.push({
        title: newTitle,
        isChecked: false
      });
      this.newItemTitle = '';
      this.saveTodo(); //ブラウザに保存
    },
todo.js
    deleteTodo: function(){
      this.items = this.items.filter(function (item) {
        return item.isChecked === false; //
      });
      this.saveTodo(); //ブラウザに保存
    },

保存出来ているかどうかはデベロッパーツールの applicationタブの左側にある Storage > localStorage > file:// と探って選択したときに右側に items が保存されているのが見えるはずです。

あとは初期表示時に localStorage から配列を読み出すようにします。

データをブラウザから読み出す

下記をmethodsオプション内に追加します。

todo.js
    loadTodo: function(){
      this.items = JSON.parse( localStorage.getItem('items') );
      if( !this.items ){
        this.items = [];
      }
    },

dataオプション内の配列itemsにブラウザ内のデータを持ってくるだけです。
ブラウザ内のデータは単なる文字列なので、JSON.parseで配列に整えます。
ブラウザにデータがないときは空の配列となるように [] を入れ込みます。

初期表示時に動く処理は mounted オプションで指定できるので、下記を methods オプションの下に追加します。

todo.js
  mounted: function(){
    this.loadTodo();
  },

CODEPEN

これでリロードしてもリストがリセットされることはなくなりました。

このハンズオンでVue.jsに興味を持ったらひとまず公式サイトのガイドで手を動かしてみるのをオススメします。ちょっと長いですが、かなりわかりやすいです!

https://jp.vuejs.org/v2/guide/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした