クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【Vue.js】localStorageと連携したTodoリストを読み解く(HTML、CSS編)

Vue.jsで単純なものを作るとリロードしたときに初期状態に戻ってしまって儚い(はかない)。。かといってデータベースと連携したものをつくるのはまだハードルが高い。前段階として以前にも触れたLocalstrageと連携して値を保存してみたい。公式サイトのサンプルにlocalStorageと連携したTodoリストがあったのでコードを読み解いてみました。それではどーぞ!

【目次】

※参考:【JS】ローカルストレージの値をリンク先のフォームのinputタグのvalueに渡す! - クモのようにコツコツと

Todoリスト完成品

まずは完成品から。公式サイトのこちらのサンプルにある「TodoMVC」を元にしている。

※参考:TodoMVC — Vue.js

See the Pen Vue de Localstarage by イイダリョウ (@i_ryo) on CodePen.

テキストを日本語にしてみた。(敬意を評してフッターのクレジットはそのまま残した)

  • 「やること」に何か予定を入れると下に追加される
  • 予定チェックを入れると打ち消し線が入って完了済みになる
  • 下のタブで「すべて」「未完了」「完了済み」の絞り込み表示ができる
  • 右下の「すべて削除」で初期状態にもどる

f:id:idr_zz:20190728071409j:plain

何か予定を入れた状態でリロードしていただくと、その状態がそのまま保持された!

localStorageに保存されているためだ。

f:id:idr_zz:20190728074921j:plain

//キー名
todos-vuejs-2.0

//値
[
  { "id":1, "title":"買い物に行く", "completed":false},
  {"id":2, "title":"本当に行く", "completed":false},
  {"id":3, "title":"絶対にいく", "completed":false}
]

キー名はtodos-vuejs-2.0で値は配列の中にJSON形式で入っている。

データはブラウザの中に保存されているため別のブラウザや端末には値を連携できないが、リロードによる「儚さ(はかなさ)」はだいぶ薄れたw

HTMLコード

全体像

まずは大枠のタグ構成から見ていく。

<section class="todoapp">
  <header class="header">
    <!--やること入力欄(省略)-->
  </header>
  <section class="main" v-show="todos.length" v-cloak>
    <!--やることリスト本体(省略)-->
  </section>
  <footer class="footer" v-show="todos.length" v-cloak>
    <!--タブメニュー(省略)-->
  </footer>
</section>
  • Todoリスト全体はtodoapp
  • .headerはやることの入力欄
  • .mainはやることリスト本体でv-showv-cloakがある。
  • . footerはタブメニューでここにもでv-showv-cloakがある。

初期状態では.main. footerは非表示。v-showでその設定をしてそう。v-showv-ifと同様、Vue.jsの独自属性で分岐処理。

※参考:【Vue.js】イベント(v-on)、分岐(v-show、v-if)、ループ(v-for)をやってみた! - クモのようにコツコツと

v-cloakはCSSに関連する属性で後述する。

やること入力欄

やること入力欄に当たる.headerから見ていく

  <header class="header">
    <h1>やること</h1>
    <input class="new-todo"
      autofocus autocomplete="off"
      placeholder="なにする?"
      v-model="newTodo"
      @keyup.enter="addTodo">
  </header>
  • inputタグにv-model属性と@keyup.enterがある。

autofocusautocompleteは馴染みが薄かったが、フォーム読み込み時のフォーカス設定をするinputタグデフォルトの属性だった。

※参考:<input autofocus>-HTML5タグリファレンス

※参考:<input autocomplete>-HTML5タグリファレンス

v-modelはVue.jsの独自属性でデータバインディング(フォームの入力値をリアルタイムに反映する)。

※参考:【Vue.js】v-modelデータバインディング事始め(リンダリンダの歌詞を変えてオリジナルラブソングを作る) - クモのようにコツコツと

@はVue.jsの省略記法でv-onのこと。

※参考:テンプレート構文 — Vue.js

v-onはイベント設定の属性。

※参考:【Vue.js】イベント(v-on)、分岐(v-show、v-if)、ループ(v-for)をやってみた! - クモのようにコツコツと

keyup.enterということでエンターキーを押した時のイベントと紐づいている。

この入力欄に打った内容がリアルタイムに下のやることリストに表示されたり、キーを叩いた時に発火するイベントが書かれている。

やることリスト本体

次、やることリスト本体に当たる.main

<section class="main" v-show="todos.length" v-cloak>
    <input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone">
    <label for="toggle-all"></label>
    <ul class="todo-list">
      <li v-for="todo in filteredTodos"
        class="todo"
        :key="todo.id"
        :class="{ completed: todo.completed, editing: todo == editedTodo }">
        <!--リストの中身(省略)-->
      </li>
    </ul>
  </section>
  • #toggle-allv-modelがある。
  • lavelタグは#toggle-allと紐づいている
  • .todo-listはulタグでやることリスト本体
  • .todoがはliタグでリストの1件単位。v-forでループ。
    :keyで入力内容を追跡。:classでclass名を指定。

v-modelは前述の通りデータバインディング。このinputタグは「全て選択」のチェックボックスなのでチェックの切替に対する反映。

v-forはVue.jsの独自属性でループ設定。これでリストが件数分表示される。

※参考:【Vue.js】イベント(v-on)、分岐(v-show、v-if)、ループ(v-for)をやってみた! - クモのようにコツコツと

:はVue.jsの省略記法でv-bindのこと。

※参考:テンプレート構文 — Vue.js

v-bindはVue.jsの独自属性でタグの属性を指定できる。

※参考:【Vue.js】v-bindでclass名を動的に追加、削除する!(北斗の拳の名言編) - クモのようにコツコツと

:keyは入力内容の追跡だがv-forの実行のために必要とのこと。

※参考:v-forで配列を表示する[v-bind:key][index][Vue.js]

:classはclassの指定だが値にcompletededitingとあるので完了済や編集中で見た目を変える設定と紐づいていそう。

リストの中身

次、リスト(li)の中身。

      <li v-for="todo in filteredTodos"
        class="todo"
        :key="todo.id"
        :class="{ completed: todo.completed, editing: todo == editedTodo }">
        <div class="view">
          <input class="toggle" type="checkbox" v-model="todo.completed">
          <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
          <button class="destroy" @click="removeTodo(todo)"></button>
        </div>
        <input class="edit" type="text"
          v-model="todo.title"
          v-todo-focus="todo == editedTodo"
          @blur="doneEdit(todo)"
          @keyup.enter="doneEdit(todo)"
          @keyup.esc="cancelEdit(todo)">
      </li>
  • divタグの. view 、inputタグの. editがある。
  • . viewの中にはinputタグの. class、labelタグ、buttonタグの. destroyがある。
  • . classv-modelがある。
  • labelタグには@dblclickがある。中身はマスタッシュタグ{{ }}
  • buttonタグの. destroyには@clickがある。
  • inputタグの.editにはv-modelv-todo-focus@blur@keyup.enter@keyup.escがある。

さすがにここはリスト自体にあたるのでVue.jsの属性が連続コンボ!v-model@v-onの省略記号)は前述の通り。

v-todo-focustodo名前的にもVue.jsのデフォルトの属性ではなさそう。調べたところVue.jsではカスタムでディレクティブを設定できるようだ。

※参考:カスタムディレクティブ — Vue.js

inputタグの.toggleは完了にするチェックボックスなのでv-modelでリアルタイムにそれが反映する。

labelはマスタッシュタグ{{ }}でリストのテキストが表示されるが中にダブルクリックのイベントが紐づいている。

buttonタグの. destroyにはクリックイベントが仕掛けられていて、クリックすると削除になる。

inputタグの.editだがここは文字入力がないので謎の存在だった。上のlabelをダブルクリックすると文字が変更できるようになる!それがこinputタグだ。

@blurはフォーカスが外れた時のイベント。

※参考:【Vue.js】イベントハンドリングをサンプルを作りながら理解する - Qiita

他にエンターキーやESCキーのイベントも書かれている。

タブメニュー

最後に. footer。タブメニューの部分。

 <footer class="footer" v-show="todos.length" v-cloak>
    <span class="todo-count">
      <strong>{{ remaining }}</strong> {{ remaining | pluralize }} 未完了
    </span>
    <ul class="filters">
      <li><a href="#/all" :class="{ selected: visibility == 'all' }">すべて</a></li>
      <li><a href="#/active" :class="{ selected: visibility == 'active' }">未完了</a></li>
      <li><a href="#/completed" :class="{ selected: visibility == 'completed' }">完了済み</a></li>
    </ul>
    <button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
すべて削除
    </button>
  </footer>
  • spanタグ.todo-countの中にマスタッシュタグがある
  • ulタグ. filtersはタブメニュー部分。中のliタグがタブ
  • それぞれのliタグ中のaタグには:classがある。
  • 最後のbuttonタグclear-completedは全削除ボタン。@clickv-showがある。

spanタグのマスタッシュタグ{{ }}でリストの件数が表示される。

リストがタブメニューで:classで選択された時のclass設定が紐付けられている。

buttonタグは全削除ボタンでクリックイベントが仕掛けられている。

CSS

Vue.js固有の設定のみピックアップ

/*マスタッシュの非表示設定*/
[v-cloak] { display: none; }

/* index.cssのスタイル(後略)*/

v-cloakはページ読み込み時にマスタッシュ{{ }}が一瞬表示されないようにする設定。プロパティを角カッコ[ ]で囲っているのが特徴的。

※参考:【Vue.js】{{ … }} を表示させない(v-cloak) | 西住工房

以降はサンプルのindex.cssのスタイルで一般的な内容なので省略するが、下記のような参考になるスタイルが見受けられた。

  • チェックマークのSVG画像をDeta URIで直接埋め込む
  • 「完了済み」に打ち消し線(line-through)を設定する

次回、JS編に続く…

長くなったので2回に分けます。次回、JS編です。

HTMLを見た限り、v-cloakとカスタムディレクティブは初めてだったけど基本はこれまでに触れてきた基本的なv-系の組み合わせなことがわかりました。

v-と省略記号@:を覚えておけばVue.jsの独自属性は識別しやすそう。

次回はいよいよ、JSコードの中にlocalStrageに関する処理が見つかると思うので楽しみです。それではまた!

※続き書きました!

www.i-ryo.com


※参考:Vue.jsの習得のためにやったことまとめ
qiita.com