Vue.jsミニハンズオン(TODOリスト作成)
AngularでもReactでもriot.jsでも満足できなかったひとに、ぴったりフィットなJSフレームワーク「Vue.js」のざっくりハンズオンです。
このハンズオンではnode.jsのパッケージは使わず、Google ChromeとテキストエディタがあればOKです。
Vue.jsミニハンズオンのシリーズは以下を公開しています。
今回の目標
目標はシンプルなTODOリストの作成です。
WebStorageに保存してリロードしてもデータを保持できるようにしてます。
Chromeの拡張機能を入れる
デベロッパーツールでデータを見やすくするためChromeの拡張機能「Vue.js devtools」を入れます。
Vue.jsを使っているページでデベロッパーツールを表示すると、Elements, Console...の並びにVueが増えているはずです。
データを表示してみる
データを単純に表示します。Vue.jsのデータ表示の基本はひとまずこれだけ。
- HTMLの内容にデータを表示するときは
{{ データの名前 }}
を使用します。 - JavaScriptで
new Vue({ el: '#id属性値', data: { データを設定 } })
でVueインスタンスをつくります。
<!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://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="./app1.js"></script>
</body>
</html>
const vm = new Vue({
el: '#app',
data: {
message: 'Hellor Vue.js!',
}
})
デベロッパーツールのVueタブをクリックして、<Root>
と書いてあるところをクリックすると右側にデータが出てきます。
(上記のCODEPENのリンク先を見ている場合は右上の[Change View]-[Debug Mode]をクリックするとデベロッパーツールを表示したときにVueタブが表示されます。お試しあれ。)
<Root>
の横に $vm0
とか表示されているのを確認して(番号が違うかもです)、Consoleタブで $vm0.message
と打つとちゃんと値がとれます。
また、vm
という変数にVueインスタンスを入れているので vm.message
と入れても値がとれます。
データバインディングしてみる
ではテキストボックスに入力したデータとp要素に表示するデータをバインディングしてみましょう。データをバインディングするには対象のHTML要素に v-model
というディレクティブを追加します。
ディレクティブはVue.jsが利用する v-
という接頭辞が付いた特別な属性を指します。
<div id="app">
<input type="text" v-model="message"> <!-- 追加 -->
<p>{{ message }}</p>
</div>
これだけです。テキストボックスの中のテキストを編集するとp要素の中のテキストも同時に変わります。デベロッパーツールのVueタブの<Root>
をクリックしたときに見えるデータも同時に変わります。
チェックボックスはinput要素に v-model="true"
であればチェックがつき、 v-model="false"
であればチェックがはずれます。
<div id="app">
<input type="text" v-model="message">
<input type="checkbox" v-model="isChecked"> <!-- 追加 -->
<p>{{ message }} {{ isChecked }}</p>
</div>
const vm = new Vue({
el: '#app',
data: {
message: 'Hellor Vue.js!',
isChecked: true // 追加
},
})
リストを表示してみる
シンプルなtodoリストを作るのが今回の目標なのでリスト表示をしてみます。
リスト表示には対象のHTML要素に v-for
というディレクティブを使います。
v-for
に指定している items
は data
オプション内にある配列です。
item
は配列内のデータの1セットを示す名前です。この名前はどうつけてもOKです。
配列を複数形でつけて、このデータの1セットは単数形にするのはよくある名前の付け方です。
<div id="app">
<ul>
<li v-for="item in items">{{ item.title }}</li>
</ul>
</div>
const vm = new Vue({
el: '#app',
data: {
items: [
{ title: '項目1', id: 1 },
{ title: '項目2', id: 2 },
{ title: '項目3', id: 3 },
]
},
})
クリックイベントをしかける
クリックイベントで何かしら関数が動くようにしてみます。
イベントは対象のHTML要素に v-on:click
ディレクティブを入れます。v-on
がイベント、 click
はクリックイベントを示します。値には関数名やJavaScriptの文を入れることができます。
関数はJavaScriptで methods
オプションの中に書きます。
dataオプション内のデータは this.データ名
で参照できます。
<div id="app">
<p>{{ counter }}</p>
<button v-on:click="add">追加(関数)</button>
<button v-on:click="counter++">追加(JavaScriptの文)</button>
</div>
const vm = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add: function(){
this.counter++
},
},
})
TODOリストを作成する
おおまかに必要なことは学びました。TODOリストを作っていきましょう。
TODOリストには下記の機能があるとします。
- TODOの表示
- TODOの追加
- TODOのチェック
- チェックしたTODOの削除
TODOの表示
まず「TODOの表示」をしましょう。label要素とチェックボックスも入れておきます。
<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リストの内容
- チェック済み判定フラグ
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 },
]
},
})
テキストをクリックしてもチェックされるのがいい感じです。
TODOの追加
TODOの追加は「テキストボックスに文字を入力してEnterキーを押したら、配列 items
に新しく項目を追加する」という処理です。
まずはHTMLの <div id="app">
内に下記を追加します。
<p>
<input type="text"
placeholder="TODOを入力しましょう!"
v-model="newItemTitle"
v-on:keyup.enter="addTodo"></p>
入力した文字は newItemTitle
に入ります。
そしてEnterキーを押すと addTodo
という関数に渡されます。
ではJavaScriptを書きます。
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キーを押したあとに、テキストボックス内にまだ文字が残っているので文字を消しましょう。
addTodo: function(){
this.items.push({
title: this.newItemTitle,
isChecked: false
});
this.newItemTitle = ''; //追加
},
だいぶアプリっぽくなりましたね。
TODOのチェック
項目がチェックされているときはlabel要素にclass属性を設定して、打ち消し線が入るようにしてみましょう。
class属性を設定するかしないかは
-
v-bind:class="done: true"
であればclass="done"
-
v-bind:class="done: false"
であればclass=""
となります。チェックをしているかどうかは isChecked
で取れるので ``v-bind:class="done: isChecked"`と設定します。
<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要素内に打ち消し線を表示するスタイルを追加しましょう。
<style>
.done { text-decoration: line-through; }
</style>
チェックしたTODOの削除
チェックしたリストがいつまでものこっているのは邪魔っぽいのでまとめて消す機能をつけてみます。
div#app
内に下記の button
要素をを追加します。
<button v-on:click="deleteTodo()">チェック済みの項目を削除する</button>
methods
オプション内に deleteTodo
関数を追加します。
その中で filter
メソッドを使って配列を更新します。
deleteTodo: function(){
this.items = this.items.filter(function (item) {
return item.isChecked === false; //
});
},
これでTODOリストの基礎的な表示ができました。
再描画でデータがリセットされるのを防ぐ
再描画するとリストがリセットされてしまうので、データをブラウザに保存し、初期表示時に利用してみます。
データをブラウザに保存する
データをブラウザに保存するときは localStorage
に items
という配列をまるごと保存します。methodsオプションに下記を追加します。
saveTodo: function(){
localStorage.setItem('items', JSON.stringify(this.items));
},
localStorage
に保存するのは localStorage.setItem('フィールド名': 値)
で簡単に実装できます。ただし、配列のままでは入れることができず、文字列に変換する必要があるので JSON.stringify
で変換します。
ということで項目を追加したときにブラウザに保存するため、関数を呼び出します。
addTodo: function(){
this.items.push({
title: this.newItemTitle,
isChecked: false
});
this.newItemTitle = '';
this.saveTodo(); //ブラウザに保存
},
deleteTodo: function(){
this.items = this.items.filter(function (item) {
return item.isChecked === false; //
});
this.saveTodo(); //ブラウザに保存
},
チェックボックスをつけたり外したりしたときにも配列の中身が変わるので、関数を呼び出します。
<li v-for="item in items">
<label v-bind:class="{ done: item.isChecked }">
<!-- チェックボックスに v-on:change="saveTodo" を追加 -->
<input type="checkbox" v-model="item.isChecked" v-on:change="saveTodo"> {{ item.title }}
</label>
</li>
保存出来ているかどうかはデベロッパーツールの application
タブの左側にある Storage > localStorage > file://
と探って選択したときに右側に items
が保存されているのが見えるはずです。
あとは初期表示時に localStorage
から配列を読み出すようにします。
データをブラウザから読み出す
下記をmethodsオプション内に追加します。
loadTodo: function(){
this.items = JSON.parse( localStorage.getItem('items') );
if( !this.items ){
this.items = [];
}
},
dataオプション内の配列itemsにブラウザ内のデータを持ってくるだけです。
ブラウザ内のデータは単なる文字列なので、JSON.parseで配列に整えます。
ブラウザにデータがないときは空の配列となるように []
を入れ込みます。
初期表示時に動く処理は mounted
オプションで指定できるので、下記を methods
オプションの下に追加します。
mounted: function(){
this.loadTodo();
},
これでリロードしてもリストがリセットされることはなくなりました。
このハンズオンでVue.jsに興味を持ったらひとまず公式サイトのガイドで手を動かしてみるのをオススメします。ちょっと長いですが、かなりわかりやすいです!