Vuetify.js 2.2 UIコンポーネントを調べたときの補足

# 概要

Vuetify.js 2.2のUIコンポーネントの使い方を調べたときに得られたtips(Vue.jsのtipsも含む)を簡単にまとめました。
UIコンポーネントの使い方については別記事に作成していて末尾の補足欄に一覧を掲載しています。

**環境**

* Windows 10 Professional 1909
* Node.js 12.14.1
* Vue.js 2.6.11
* Vuetify.js 2.2.17

# tips

## [v-model] (https://jp.vuejs.org/v2/api/index.html#v-model) ディレクティブ

フォーム入力バインディングでの`input`や`textarea`のv-modelは

```vue
<input v-model="text">
```

この記述と同じ意味になります。

```vue
<input
  v-bind:value="text"
  v-on:input="text = $event.target.value"
>
```

正確にはinput要素のタイプに応じて異なるプロパティとイベントが使用されます。

> v-modelは、内部的にはinput要素に応じて異なるプロパティを使用し、異なるイベントを送出します:
* テキストと複数行テキストは、value プロパティと input イベントを使用します
* チェックボックスとラジオボタンは、checked プロパティと change イベントを使用します
* 選択フィールドは、value プロパティと change イベントを使用します

Vuetify.jsのUIコンポーネントに`value`プロパティと`input`イベントがある場合、v-modelが使用できます。

|           |value prop|input-value prop|input event|input value|
|:----------|:----|:----------|:----|:------|
|[v-alert] (https://vuetifyjs.com/en/components/alerts/) |Controls whether the component is visible or hidden.| |The updated bound model|boolean|
|[v-carousel] (https://vuetifyjs.com/en/components/carousels/) |The designated model value for the component.| |The updated bound model|number|
|[v-chip] (https://vuetifyjs.com/en/components/chips/) |The value used when a child of a v-chip-group. |Controls the active state of the item. This is typically used to highlight the component.| The updated bound model|boolean|
|[v-dialog] (https://vuetifyjs.com/en/components/dialogs/) |Controls whether the component is visible or hidden.| |The updated bound model|boolean|
|[v-navigation-drawer] (https://vuetifyjs.com/en/components/navigation-drawers/)|Controls whether the component is visible or hidden.| |The updated bound model|boolean|
|[v-pagination] (https://vuetifyjs.com/en/components/paginations/)|Current selected page| |The updated bound model|number|
|[v-rating] (https://vuetifyjs.com/en/components/ratings/) |The rating value | |Emits the rating number when this value changes|number|
|[v-snackbar] (https://vuetifyjs.com/en/components/snackbars/) |Controls whether the component is visible or hidden.| |The updated bound model|boolean|
|[v-treeview] (https://vuetifyjs.com/en/components/treeview/) |Allows one to control which nodes are selected. The array consists of the item-key of each selected item. Is used with @input event to allow for v-model binding.| |Emits the array of selected items when this value changes |array|

## [v-on] (https://jp.vuejs.org/v2/api/index.html#v-on) ディレクティブ

* 参考: [Meaning of v-slot:activator=“{ on }”] (https://stackoverflow.com/questions/55188478/meaning-of-v-slotactivator-on/55194478)

Vuetify.jsのUIコンポーネントの中にはアクティベータスロットを定義するものがあります。
例えばv-dialogコンポーネントは、下記のようにダイアログ表示のトリガーになるボタンをアクティベータスロットで定義することができます。

```vue
<v-dialog v-model="dialog">
  <template v-slot:activator="{ on }">
    <v-btn v-on="on">Open Dialog</v-btn>
  </template>
  <!-- ダイアログの定義 省略 -->
</v-dialog>
```

このコードの`v-slot:activator="{ on }"`と`v-on="on"`という部分は、Vue.js 2.4.0からサポートされているオブジェクト構文と呼ばれる記法です。

```vue
<!-- オブジェクト構文 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
```

## [v-for] (https://jp.vuejs.org/v2/api/index.html#v-for) ディレクティブと変数展開

同一のv-for内で複数の要素にkeyをバインディングする場合、以下のコードでそれぞれの要素のkeyにindexをバインドすると

```vue
<template v-for="(item, index) in items">
  <v-divider inset :key="index"></v-divider>
  <v-list-item :key="index" link>
    <!-- 省略 -->
  </v-list-item>
</template>
```

実行時にブラウザのコンソールにこのようなWARNが出力されます。（表示だけであれば特に問題ないように見えるのですが、バインドしたデータが更新される場合は問題が起きるかもしれません）

```text
[Vue warn]: Duplicate keys detected: '0'. This may cause an update error.
```

このため、以下のようにそれぞれのkeyに異なる値をバインドするようにしました。

```vue
<template v-for="(item, index) in items">
  <v-divider inset :key="`div-${index}`"></v-divider>
  <v-list-item :key="`list-${index}`" link>
    <!-- 省略 -->
  </v-list-item>
</template>
```

この書き方はJavaScript(ES2015)から使える、文字列中に変数展開ができるテンプレート構文といいます。

```javascript
:key="`div-${index}`"
```

## [v-show] (https://jp.vuejs.org/v2/api/index.html#v-show) ディレクティブとVuetifyのcss visibility

v-showやv-ifで条件付きレンダリングを行えます。それぞれの特徴は下記に引用した通りです。

> v-if
> 遅延描画(lazy)です。 初期表示においてfalseの場合、何もしません。条件付きブロックは、条件が最初にtrueになるまで描画されません。

> v-show
> 要素は初期条件に関わらず常に描画され、シンプルなCSSベースの切り替えとして保存されます。

Vuetify.jsには`$vuetify.breakpoint`というサービスオブジェクトがあり、このオブジェクトのプロパティを併用することで特定のブレイクポイントのときに要素を表示する／表示しないといった実装ができます。

以下の例ではブレイクポイントがLG以上でv-cardコンポーネントが表示され、MD以下で非表示になりますがv-showの場合はスタイルに"display: none;"が適用され、v-ifの場合は要素自体が描画されません。

```vue
<v-card v-show="$vuetify.breakpoint.lgAndUp">
  <!-- 省略 -->
</v-card>

<v-card v-if="$vuetify.breakpoint.lgAndUp">
  <!-- 省略 -->
</v-card>
```

さらに、Vuetify.jsではCSSで要素の表示／非表示をコントロールできます。どちらもブレイクポイントがLG以上でv-cardコンポーネントが表示されます。

```vue
<v-card class="d-none d-lg-block">
  <!-- 省略 -->
</v-card>

<v-card class="hidden-md-and-down">
  <!-- 省略 -->
</v-card>
```

### visibility (可視性)

* 参考: [Display helpers] (https://vuetifyjs.com/ja/styles/display/)

|visibiity                |breakpoint code         ||
|:------------------------|:-----------------------|:----|
|`.d-none`                |`xs`                    |xs以上で非表示。(つまり全てのブレイクポイント)|
|`.d-{breakpoint}-none`   |`sm`, `md`, `lg`, `xl`  |指定したコード以上で非表示。|
|`.d-block`               |`xs`                    |xs以上で表示。(つまり全てのブレイクポイント) |
|`.d-{breakpoint}-block`  |`sm`, `md`, `lg`, `xl`  |指定したコード以上で表示。  |

`none`や`block`の部分には、以下の値を使用できます。基本的にはCSSの`display`の値です。

|value        |display                          |
|:------------|:--------------------------------|
|none         |display: none !important;        |
|inline       |display: inline !important;      |
|inline-block |display: inline-block !important;|
|block        |display: block !important;       |
|table        |display: table !important;       |
|table-cell   |display: table-cell !important;  |
|table-row    |display: table-row !important;   |
|flex         |display: flex !important;        |
|inline-flex  |display: inline-flex !important; |

### hidden

`{breakpoint}` : `xs`、`sm`、`md`、`lg`、`xl`
`{condition}` : `only`、`and-down`、`and-up`

```text
hidden-{breakpoint}-{condition}
```

## Vuetify object

### [Breakpoint service object] (https://vuetifyjs.com/en/customization/breakpoints/)

```text
$vuetify.breakpoint
```

|property     |type       |description|
|:------------|:----------|:----------|
|name         |string     |現在のbreakpointの名前|
|height       |number     |高さ(px) |
|width        |number     |幅(px)   |
|xs           |boolean    |現在のbreakpointがxsの時はtrue|
|sm           |boolean    |現在のbreakpointがsmの時はtrue|
|md           |boolean    |現在のbreakpointがmdの時はtrue|
|lg           |boolean    |現在のbreakpointがlgの時はtrue|
|xl           |boolean    |現在のbreakpointがxlの時はtrue|
|xsOnly       |boolean    | ? |
|smOnly       |boolean    | ? |
|mdOnly       |boolean    | ? |
|lgOnly       |boolean    | ? |
|xlOnly       |boolean    | ? |
|smAndDown    |boolean    |現在のbreakpointがsm以下の時はtrue|
|smAndUp      |boolean    |現在のbreakpointがsm以上の時はtrue|
|mdAndDown    |boolean    |現在のbreakpointがmd以下の時はtrue|
|mdAndUp      |boolean    |現在のbreakpointがmd以上の時はtrue|
|lgAndDown    |boolean    |現在のbreakpointがlg以下の時はtrue|
|lgAndUp      |boolean    |現在のbreakpointがlg以上の時はtrue|
|resizeTimeout  |number   | ? |
|scrollBarWidth |number   |スクロールバーの幅(px) |
|thresholds.xs  |number   |xsと判断する横幅(px)の閾値 |
|thresholds.sm  |number   |smと判断する横幅(px)の閾値 |
|thresholds.md  |number   |mdと判断する横幅(px)の閾値 |
|thresholds.lg  |number   |lgと判断する横幅(px)の閾値 |

### [Application service object] (https://vuetifyjs.com/en/components/application/#application-service)

```text
$vuetify.application
```

|property     |type       |description|
|:------------|:----------|:----------|
|bar          |number     | |
|top          |number     | |
|left         |number     | |
|insetFooter  |number     | |
|right        |number     | |
|bottom       |number     | |
|footer       |number     | |

## Styles

### Elevation helpers

`{n}`には0から24までの数値を指定できます。

```text:format
elevation-{n}
```

UIコンポーネントの中には`elevation`プロパティを持つものがあり、以下のようにelevationを指定することができます。

```vue
<v-card elevation="10">
  <!-- 省略 -->
</v-card>
```

このようなUIコンポーネント以外でも、CSSでelevationを指定することができます。

```html
<div class="d-block elevation-10">
  block text
</div>
```

### Spacing helpers

下記の命名規約のCSSクラスで要素にマージンやパディングを設定できます。

```text:format
{property}{direction}-{size}
```

```html
<div class="pa-3">
  <!-- 上下左右に12pxのパディングを設定する -->
</div>
```

**property**

|property |        |
|:-------:|:-------|
|m        |margin  |
|p        |padding |

**direction**

|direction |        |
|:--------:|:-------|
|t         |top     |
|b         |bottom  |
|l         |left    |
|r         |right   |
|s         | ?      |
|e         | ?      |
|x         |left and right |
|y         |top and bottom |
|a         |all directions (top and bottom and left and right) |

**size**

4px単位で増分

|size  |px      |                |
|:----:|:-------|:---------------|
|0     |0       |margin, padding |
|1     |4px     |margin, padding |
|2     |8px     |margin, padding |
|3     |12px    |margin, padding |
|4     |16px    |margin, padding |
|5     |20px    |margin, padding |
|6     |24px    |margin, padding |
|7     |28px    |margin, padding |
|8     |32px    |margin, padding |
|9     |36px    |margin, padding |
|10    |40px    |margin, padding |
|11    |44px    |margin, padding |
|12    |48px    |margin, padding |
|n1    |4px     |margin |
|n2    |8px     |margin |
|n3    |12px    |margin |
|n4    |16px    |margin |
|n5    |20px    |margin |
|n6    |24px    |margin |
|n7    |28px    |margin |
|n8    |32px    |margin |
|n9    |36px    |margin |
|n10   |40px    |margin |
|n11   |44px    |margin |
|n12   |48px    |margin |
|auto  |auto    | |

## 関数型コンポーネント

* 参考 : [関数型コンポーネント] (https://jp.vuejs.org/v2/guide/render-function.html#%E9%96%A2%E6%95%B0%E5%9E%8B%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88)

Vue.jsの関数型コンポーネントとは

* 状態を持たない (リアクティブデータが無い)
* インスタンスを持たない (thisのコンテキストが無い)

という特徴を持つ軽量コンポーネントです。

Vuetify.jsのコンポーネントの中には関数型コンポーネントとして実装されているものがあります。
簡単な見分け方として、オフィシャルサイトのAPIドキュメントでPROPSやEVENTなどが無いコンポーネントは関数型コンポーネントとして考えて良いとおもいます。

例えばv-card-textコンポーネントは関数型コンポーネントですが、図のようにAPIドキュメントにはPROPSやEVENTがありません。
![v-card-text.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22772/83fecf95-ee2f-0a87-a42a-4da2498f4cf0.png)

# upgrade from 2.2.x to 2.3.x

2020年6月12日にVuetify.js 2.3.0がリリースされました。
Upgrade Guideに記載の通り（下記引用）、後方互換が考慮されているので基本的にソースコードの必須ではないということです。

> [Upgrade guide] (https://github.com/vuetifyjs/vuetify/releases#user-content-upgrade-guide)
> Liberator contains a few small upgrades for deprecated functionality. Don't worry, these upgrades are **BACKWARDS** compatible. If you experience anything that suggests otherwise, please reach out to us in our [Discord community](https://community.vuetifyjs.com/).

また、実行時にコンソールに下図のようなメッセージが表示されるので修正箇所の把握は簡単です。
![Screenshot_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22772/a9bf893a-cd15-d1c2-f2ee-5281bd172ab4.png)


# 補足

## リソース

Vuetify.jsのUIコンポーネントを調べるときに参考にしたサイトです。

### オフィシャル情報

* [Quick start - Vuetify.js] (https://vuetifyjs.com/en/getting-started/quick-start)
* [vuetifyjs / vuetify - github.com] (https://github.com/vuetifyjs/vuetify)

#### チートシート

* [VUETIFY FUNDAMENTALS CHEAT SHEET] (https://cdn.vuetifyjs.com/files/vuetify-fundamentals-cheat-sheet.pdf) (PDF)

#### マイグレーション

* [Migrating from v1.5.x to v2.0.x] (https://github.com/vuetifyjs/vuetify/blob/3d1483d789a4fb0589bc13667cd1a83c5d8cef7d/MIGRATION.md)

#### リリース

* [Release Note] (https://github.com/vuetifyjs/vuetify/releases)
* [Release v2.2.0 Tigris] (https://github.com/vuetifyjs/vuetify/releases/tag/v2.2.0)
* [Release v2.1.0 Vanguard] (https://github.com/vuetifyjs/vuetify/releases/tag/v2.1.0)
* [Release v2.0.0 Arcadia] (https://github.com/vuetifyjs/vuetify/releases/tag/v2.0.0)

### アンオフィシャル

* [Vuetify - Material Component Framework for Vue.js | reddit.com] (https://www.reddit.com/r/vuetifyjs/)
* [Vuetify.js Questions - Stack Overflow] (https://stackoverflow.com/questions/tagged/vuetify.js)

### マテリアルデザイン

* [Homepage - Material Design] (https://material.io/)
* [Material Design Icons] (https://cdn.materialdesignicons.com/4.5.95/)
* [Bootstrap] (https://getbootstrap.com/)

## UIコンポーネントの使い方の記事一覧

Vuetify.jsのUIコンポーネントを調べたときに作成した記事の一覧です。

* [Vuetify.js 2.2のSheet, Alert, Avatar, Badge, Iconコンポーネントについて] (https://qiita.com/rubytomato@github/items/9829f7f14887a1abb834)
* [Vuetify.js 2.2のPagination, Stepperコンポーネントについて] (https://qiita.com/rubytomato@github/items/4db52d9f0f61955b998a)
* [Vuetify.js 2.2のCardコンポーネントについて] (https://qiita.com/rubytomato@github/items/811e534805bbf659b5af)
* [Vuetify.js 2.2のButtonコンポーネントについて] (https://qiita.com/rubytomato@github/items/890d59804978aa959e9c)
* [Vuetify.js 2.2のChipコンポーネントについて] (https://qiita.com/rubytomato@github/items/fe4d3712f76da7c06f52)
* [Vuetify.js 2.2のDialogコンポーネントについて] (https://qiita.com/rubytomato@github/items/d60b92906c2a9ce7f161)
* [Vuetify.js 2.2のMenu, Tooltipコンポーネントについて] (https://qiita.com/rubytomato@github/items/9bd8dc197f9ab406bead)
* [Vuetify.js 2.2のListコンポーネントについて] (https://qiita.com/rubytomato@github/items/784a0eb013a9de1937bd)
* [Vuetify.js 2.2のGridSystemについて] (https://qiita.com/rubytomato@github/items/07fe07e64482f8f03ef3)
* [Vuetify.js 2.2のFooterコンポーネントについて] (https://qiita.com/rubytomato@github/items/b561453df3ea48c5a95a)
* [Vuetify.js 2.2のApp Barsコンポーネントについて] (https://qiita.com/rubytomato@github/items/f901165e32a24fcb44d8)
* [Vuetify.js 2.2のNavigation drawersコンポーネントについて] (https://qiita.com/rubytomato@github/items/1c75c2b98b8bae410ee1)
* [Vuetify.js 2.2のBottom navigationコンポーネントについて] (https://qiita.com/rubytomato@github/items/08f7d5387e6e5ff38677)
* [Vuetify.js 2.2のApplicationについて] (https://qiita.com/rubytomato@github/items/47ead9f00cec32dcd72f)

**Vuetify.js 2.3.x**

* [Vuetify.js 2.3のData iteratorsコンポーネントについて] (https://qiita.com/rubytomato@github/items/ffb8815bd715c11deaa7)

## ソースコード

記事にしたコードはGitHubのリポジトリで、ビルド結果はGitHub Pagesで公開しています。

* [rubytomato / demo-vue-vuetify-v2] (https://github.com/rubytomato/demo-vue-vuetify-v2)
    * GitHub Pages : https://rubytomato.github.io/demo-vue-vuetify-v2/
* [rubytomato / demo-vue-vuetify-v2-app] (https://github.com/rubytomato/demo-vue-vuetify-v2-app)
    * GitHub Pages : https://rubytomato.github.io/demo-vue-vuetify-v2-app/
* [rubytomato / demo-vue-vuetify-v2-tables] (https://github.com/rubytomato/demo-vue-vuetify-v2-tables)