VuetifyでTodoリストアプリに使えそうなコンポーネント

# 背景
Vueで作ったシンプルなTodoリストをお洒落にするべく、Todoリストで使えそうなVuetifyのコンポーネントを下調べする。

## 作ったTodoアプリ
### Before
![vue-todo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526994/fe1c4a95-bb1d-6782-cbb2-1fbf5f3072dd.gif)

### After
![vuetify-todo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526994/aa2ab8d1-825a-9ac2-123b-91b2f43e8f7d.gif)

#### ソース
https://github.com/popy1017/vuetify-todo
#### デモ
https://popy1017.github.io/vuetify-todo/

# チェックボックス
Todoの完了などに使えそう。
[Selection control components — Vuetify.js](https://vuetifyjs.com/ja/components/selection-controls/)

## 単体のチェックボックスの場合
Vueでチェックボックスを使う場合とだいたい一緒。
value属性をつけると、チェックした時にその値が入るので、booleanを扱う際は注意。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="oNjpwEy" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-checkbox-single">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/oNjpwEy">
  v-checkbox-single</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## (おまけ)複数のチェックボックスの場合
シンプルなタグ付けとかに使えそう。
こちらもVueで使う時とだいたい同じ。
v-modelで指定する変数は配列にする。選択したチェックボックスに指定されているvalueの値が配列に入る。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="VwvyWxE" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-checkbox-multiple">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/VwvyWxE">
  v-checkbox-multiple</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## チェックボックスのアイコンを変える
on-icon属性とoff-icon属性でアイコン名を指定することで、チェックボックスのアイコンを変えることができる。
color属性でon-iconで指定したアイコンの色を変えることができる。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="RwWxgzz" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-checkbox-icon">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/RwWxgzz">
  v-checkbox-icon</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## チェックボックスのアイコンの大きさを変える
アイコンの大きさを変える属性がなかった？ので、v-iconかv-btnでクリックのたびに動的に表示するアイコンを変え擬似チェックボックスを作る。
基本的には、`<v-icon>{{iconName}}</v-icon>`として、クリックがあったら`iconName`を変える。
v-iconには数段階でアイコンの大きさを変える属性が用意されている。

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="GRpyvEp" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-large-dynamic-icon">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/GRpyvEp">
  v-large-dynamic-icon</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## その他の選択コンポーネント
### スイッチ
単体で使う場合、複数で使いたい場合ともにv-checkboxと同じように使うことができる。
<p class="codepen" data-height="320" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="WNQdXzv" style="height: 320px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-switch">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/WNQdXzv">
  v-switch</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### ラジオボタン
ラジオボタンはチェックボックス、スイッチとは少し違い`v-radio-group`コンポーネントで複数の`v-radio`コンポーネントを囲う必要がある。また、`v-radio-group`には`column`、`row`という属性が用意されており、水平方向・垂直方向に並べ替えられる。デフォルトは`column`で水平方向に並ぶ。
`v-model`でバインドした変数には、選択した`v-radio`の`value`属性に設定されている文字列が入る。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="BaoJmva" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-radio">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/BaoJmva">
  v-radio</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# 入力フォーム
Todoの新規作成や編集に必須。
[Text field component — Vuetify.js](https://vuetifyjs.com/ja/components/text-fields/)
## 基本の入力フォーム
`v-text-field`コンポーネントを用いる。**ちなみに、.lazy修飾子は効かないので注意。**
対処方法としては、`v-model.lazy="hoge"`は`:value="hoge" @change="v => hoge = v"`に置き換えられるのでそう書く。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="jObYajV" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-text-field.lazy">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/jObYajV">
  v-text-field.lazy</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## 様々なオプション
<p class="codepen" data-height="406" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="yLYpKaY" style="height: 406px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-text-field-options">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/yLYpKaY">
  v-text-field-options</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## バリデーション
こちらの記事を参考にさせていただきました。
[Vue & Vuetifyでバリデーション付きのフォームを作ってみる - Qiita](https://qiita.com/tekunikaruza/items/0a68d86084d961d632ac)
公式はこちら。[Text field component — Vuetify.js](https://vuetifyjs.com/ja/components/text-fields/#%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)

`rules`属性に、関数の配列を指定すればOK。指定する関数は、booleanかstringを返す必要がある。
ただし、`rules`属性を付与しただけでは見た目が変わるだけなので、ボタンを押した時にチェックして違反があったら実行させないとか、そもそもボタンを押せなくするとかは別にやる必要がある。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="popy1017" data-slug-hash="PoPEeow" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-text-field-validation">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/PoPEeow">
  v-text-field-validation</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### ボタン押下時にバリデーションチェックする
ボタン押下時にバリデーションを行うためには、`v-form`コンポーネントに用意された関数を利用する。
`<v-form ref="form">`とすると、すべての入力をチェックしすべてが有効なものかを判定する`this.$refs.form.validate()`が使える。また、その他の関数としては、入力状態をリセットする`this.$refs.form.reset()`、バリデーションの状態のみをリセットする`this.$refs.form.resetValidation()`が利用できる。

<p class="codepen" data-height="387" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="YzyYLbw" style="height: 387px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-text-filed-validate-btn">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/YzyYLbw">
  v-text-filed-validate-btn</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


### 入力項目が正常になるまでボタンを非活性にする
`v-form`に`v-model="変数名"`で全体のバリデーションの状態（true or false）を保持することが可能。
ボタンの活性・非活性を動的に変えるためには、`<v-btn :disable="変数名">`とすればOK。(変数がtrueのとき非活性になる。)

<p class="codepen" data-height="416" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="bGVayPO" style="height: 416px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-text-field-btn-disable">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/bGVayPO">
  v-text-field-btn-disable</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# DatePicker & TimePicker
Todoの締切日を設定するのに必要。
[Date picker component — Vuetify.js](https://vuetifyjs.com/ja/components/date-pickers/)
[Time picker component — Vuetify.js](https://vuetifyjs.com/ja/components/time-pickers/)

## DatePicker
`v-date-picker`コンポーネントを使う。

### 基本的な使い方
日本語化は`locale="jp-ja"`でできるが、各日付に`日`がついてしまって見栄えが悪い。
対処方法としては、`day-format`属性で日付テーブルに表示されるdayの文字列の書式をカスタマイズする。
こちらの記事を参考にさせていただきました。
[VuetifyのDate Pickerを日本語化したら"日"ってついてくる、やだキモい - nanisore oishisou](http://arm4.hatenablog.com/entry/2018/08/17/155344)

<p class="codepen" data-height="492" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="mdepZyL" style="height: 492px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-date-picker-ja">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/mdepZyL">
  v-date-picker-ja</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### テキストフィールドに埋め込む
`v-menu`や`v-dialog`と組み合わせることで、`v-text-field`をへのフォーカス？をトリガーに`v-date-picker`を起動させることが可能。
`v-menu`や`v-dialog`についてはあまり深く理解してないが、以下のような構造になっていると理解。

```html
<v-menu> or <v-dialog>
  <template v-slot:activator="{ on }">

    <!-- テキストフィールドとかボタンとか、トリガーとなる要素 -->
    <v-text-field v-on="on"></v-text-field>
  </template>

  <!-- templateで囲った要素でイベントが発火したときに表示したい要素 -->
  <v-date-picker></v-date-picker>

</v-menu> or </v-dialog>
```

<p class="codepen" data-height="545" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="LYpeKLe" style="height: 545px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-date-picker-in-text-field">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/LYpeKLe">
  v-date-picker-in-text-field</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## TimePicker
`v-time-picker`を使う。 [Time picker component — Vuetify.js](https://vuetifyjs.com/ja/components/time-pickers/)
使い方は`v-date-picker`とだいたい同じ。（な気がする）
12時間・24時間表記の設定は、`format="ampm(or 24hr)"`でOK。(デフォルトは`"ampm"`)
`v-date-picker`と同様、`v-menu`や`v-dialog`と組み合わせて`v-text-field`と統合することも可能。

<p class="codepen" data-height="526" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="eYpywyB" style="height: 526px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="eYpywyB">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/eYpywyB">
  eYpywyB</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## (おまけ)DateTimePicker
公式では用意されていないので、有志の方が公開してくれているものを使う。
[darrenfang/vuetify-datetime-picker: DatetimePicker component for Vuetify.js. ](https://github.com/darrenfang/vuetify-datetime-picker)
上記にオンラインデモへのリンクもある。

# Card
各Todoを1つ1つのカードとして表示したい場合に使える。(自前でcssでやってたので是非使いたい。)

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="BaoJXVQ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-card-default">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/BaoJXVQ">
  v-card-default</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

gridレイアウト(`v-row`, `v-col`)と組み合わせることで、様々なレイアウトを実現可能。(極端に画面幅を狭めると若干レイアウトが崩れる。)
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="popy1017" data-slug-hash="NWGXQEg" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="v-card-grid-layout">
  <span>See the Pen <a href="https://codepen.io/popy1017/pen/NWGXQEg">
  v-card-grid-layout</a> by popy1017 (<a href="https://codepen.io/popy1017">@popy1017</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# トランジション
Todoを削除したり、完了にして非表示にするときなど、おしゃれにリスト間を詰めるために使える。
[Transitions — Vuetify.js](https://vuetifyjs.com/ja/styles/transitions/)
Vueだと`transition`コンポーネントで囲って、`v-enter, v-enter-leave`とかのスタイルを定義していたところを、`v-fade-transition`とか`v-expand-transition`とかで囲むだけで既存のトランジションが使える。（自分でスタイル定義して、`transition`で使うこともできる。）
`transition-group`の代わりに、`group`属性をつけることでグループ化される。
CodePen上だとうまくうごかなかったのでコードを掲載。

```html
<template>
  <v-app>
    <v-container>
      <v-btn @click="insertNumber">+Insert</v-btn>
      <v-fade-transition group leave-absolute hide-on-leave>
        <v-card v-for="(n,index) in numbers" :key="n">
          <v-row align="center">
            <v-col>
              <v-card-title>{{n}}</v-card-title>
            </v-col>
            <v-col cols="2">
              <v-btn @click="removeNumber(index)" icon>
                <v-icon>mdi-trash-can-outline</v-icon>
              </v-btn>
            </v-col>
          </v-row>
        </v-card>
      </v-fade-transition>
    </v-container>
  </v-app>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      numbers: [1, 2, 3],
      currentNumber: 4
    };
  },
  methods: {
    insertNumber() {
      const id = Math.floor(Math.random() * this.numbers.length);
      this.numbers.splice(id, 0, this.currentNumber);
      this.currentNumber += 1;
    },
    removeNumber(id) {
      this.numbers.splice(id, 1);
    }
  }
};
</script>
```