2019/05/23 2019/07/07
Vue.js学習してTodoアプリを作ってみた
次の作品を作る前に、Vue.jsを勉強する必要があったため
しばらくインプットにいそしむことに。
Vue.js学習の動機
今、ウェブカツでプログラミング学習をしていまして
その中でJavaScriptのフレームワークがいくつか出てくるんですよね。
出てくるのは良いんですが
毛根が死滅しそうな難易度
・・いやマジで。
私元々JavaScript自体にもの凄く苦手感があるというか
この20年のうちに2度JS独学で学習しようとして挫折し
去年作りたい物が出来たところで
3度目の挫折をしそうになったのでウェブカツに入った
という経緯があってかるーくトラウマじみたものがあるんです。
それでも何とかフレームワークの講義まではたどり着いたのですが
まぁ見事に
Backbone.jsで禿げ上がるほど悩み
Reactは分かったような分からないような気分で通り過ぎ
Reactのreduxとやらで毛根が瞬殺された
・・一体何だったんだあれは・・( ゚д゚)
まさに「何を言っているんだこれは・・」状態でしたorz
んで、まぁそんな中で唯一
「こ、これなら何か理解できそうな気がする!」となった
Vue.jsを、初めてのJSフレームワークとして学習することにしたのです。
学習に使った教材
-
ウェブカツのJS部上級に出てたコード
動画は流し見で、結局コードを印刷してメモりつつ実際に動かして終了 -
基礎から学ぶVue.js
ねこさんが可愛いVue.jsの入門書。6章まで読了
7章以降は実際にSPAとやらが必要になったら読もうかと
取りあえず上記2つを使って2日でざっくりインプット終了。
後は作りながら理解した方が早いはず、ということで。
Vue.jsでTodoアプリを作ってみた
基礎を確認する意味で、簡単なTodoアプリを作りました。
機能としては
- 新規todoリスト作成
- リスト削除
- 実行済み、未実行をクリックで切り替える
- 部分一致によるリスト検索
- 削除時にアニメーションを入れる
詰まったところと解決策
実行済みと未実行の切り替えができねぇ!
・・何のことは無い、要素の指定方法がおかしかった。
リストのデータは配列で置き、その中にチェックされてるかどうかを判断するための
「isChecked」という要素をぶっ込んだんですね。
data: {
items: [
{ id: 1, text: "sample todo1", isChecked: false },
{ id: 2, text: "sample todo2", isChecked: true }
],
},
で、この「isCheckedを使ってmethodでtrueとfalse切り替えりゃいける!」と
リストの方はこんな感じで
<li
class="list__item"
v-for="(item,index) in filteredItems"
v-bind:key="item.id"
v-bind:class="{'is-checked': .isChecked}"
>
<i
class="fa fa-circle-thin icon-check"
v-on:click="changeStatus(item)"
></i>
<span>{{ item.text }}</span>
</li>
切り替え用のmethodはこんな風に書いてみたら
methods: {
changeStatus(item) {
isChecked = isChecked ? false : true;
},
}
・・押しても動かねぇ・・orz
devtoolで見たらv-onはちゃんと動いてるのに全く反映されねぇ・・
ここで詰まること2時間。
原因は
配列の中に入ってる要素なので、単に「isChecked」じゃなくて
配列名の「item」付けて「item.isChecked」にせな動かんわなそら!!
大体v-bind: keyのところでちゃんとitem.idって書いてるのに何故気づかなかった・・
そこを直すだけで動きました。
いやぁ思い込みって怖いですねぇホント(´・ω・`)
検索機能
まずはここを参考にして検索機能を付けてみました。
こういうときにcomputedを使うのね・・勉強になりました。
https://orizuru.io/blog/vue-js/search-function/
・・が、ここに書かれた方法を使うと
完全一致じゃないと上手いこといかない。
一部だけ一致で表示するってどうやって実装したらええんじゃー!
てことで「javascript 検索機能 一部一致」でググって出てきたのが
var text = 'sampletext';
var keyword = 'ple';
if(text.indexOf(keyword) > -1){
// 部分一致のときの処理
}
indexOf()を使うと、一致しなかったときに-1を返す、一致すると0以上の整数を返す。
なるほどこれが使えるな!
そしてこの機能は部分一致って言えばいいんだな!!←そこじゃない
これで部分一致になりました。
動作確認
#ウェブカツ 復習
— RIE🇲🇾KLゆるゆる生活 (@saika00) May 21, 2019
Vue.jsでtodoアプリ出来上がり。取りあえずコンポーネント化しなければ動作することは確認。明日はコンポーネントにしてみてどうなるかチェックして、問題無ければ一つポートフォリオ用に作品作ります。 pic.twitter.com/hoITkbpWM1
これはコンポーネント化前のやつですが
コンポーネントにしてもちゃんと動いていました。
出来上がったやつのコード
もっと良い書き方があるんじゃないかと思いつつも
取りあえずこれで動いたので上げておきます。(CSSは省略)
コンポーネントとやらを使ってみたかったので
取りあえず全部一つのコンポーネントに放り込んでみたけど
これで良かったのか・・?
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>TODO</title>
<link rel="stylesheet" href="dist/css/app.css" />
<script src="https://use.fontawesome.com/e3ae05b8e6.js"></script>
</head>
<body>
<div class="main">
<h1 class="mainTitle">TODOS</h1>
<div id="appTodo">
<todo-item></todo-item>
</div>
</div>
<script src="dist/js/bundle.js"></script>
</body>
</html>
import Vue from "vue";
Vue.component("todo-item", {
data: function() {
return {
items: [
{ id: 1, text: "sample todo1", isChecked: false },
{ id: 2, text: "sample todo2", isChecked: true }
],
nextID: 3,
todoItem: "",
searchWord: ""
};
},
computed: {
filteredItems() {
return this.searchItem(this.items, this.searchWord);
}
},
methods: {
addItem() {
this.items.push({
id: this.nextID++,
text: this.todoItem,
isChecked: false
}),
(this.todoItem = "");
},
doRemove(index) {
this.items.splice(index, 1);
},
changeStatus(item) {
item.isChecked = item.isChecked ? false : true;
},
searchItem(list, key) {
return list.filter(function(item) {
return item.text.indexOf(key) !== -1 || key === "";
});
}
},
template: `
<div>
<div class="form">
<div class="inputArea">
<input
type="text"
class="inputText"
placeholder="something todo task"
v-on:keydown.enter.shift="addItem"
v-model="todoItem"
/>
</div>
</div>
<div class="searchBox">
<i class="fa fa-search searchBox__icon"></i>
<input
type="text"
class="searchBox__input"
placeholder="something keyword"
v-model="searchWord"
/>
</div>
<ul class="list">
<transition-group name="fade">
<li
class="list__item"
v-for="(item,index) in filteredItems"
v-bind:key="item.id"
v-bind:class="{'is-checked': item.isChecked}"
>
<i
class="fa fa-circle-thin icon-check"
v-on:click="changeStatus(item)"
></i>
<span>{{ item.text }}</span>
<i
class="fa fa-trash icon-trash"
v-on:click="doRemove(index)"
></i>
</li>
</transition-group>
</ul>
</div>
`
});
new Vue({
el: "#appTodo",
data: {},
computed: {},
methods: {}
});