Vuetifyで配列型データからボタンを作り、クリックされたら色を変える

Vue.jsで楽に綺麗にコンポーネントを作れるVuetifyフレームワークですが、楽だからこそ「さらに発展させてこうしたい！（もうちょい頑張ったらCSS書かずに済みそう...）」という時にちょっと悩みます。

なので、悩んだところをちょこちょこメモ書き＆自学のために残します。誰かのお役に立てば幸いです。
フロント修行中ですので、ご指摘大歓迎です。

---

### やりたいこと

1. 配列型の中にある各文字列をボタンの表示に利用したい
2. ボタンを押したら、選択されたボタンの色が変わって欲しい
3. 押されたボタンのキー（配列内の文字列）を取得したい

### 実装例

```vuejs:template(HTML)部分
<v-btn
    v-for="(item, index) of items"
    :key="item"
    :color="selectedTemplate && index === selectedIndex ? 'warning' : null"
    @click="
        selectedTemplate = item;
        selectedIndex = index;
    "
    >
    {{ item }}
</v-btn>
```

```vuejs:script(Javascript)部分
<script>
  // Vueインスタンス内の必要部分から抜粋
    data: {
        items: [
            "A",
            "B",
            "C"
         ],
        selectedItem: null
    }
// 以下略
</script>
```

### 解説

#### 1. 配列型の中にある各文字列をボタンの表示に利用したい

- script内の `data`で配列データ `items`を定義

- template内の `v-for="(item, index) of items"`で配列データをforで回す

  - 配列内の要素 `item` と要素のインデックス `index` を取得する
  - `{{ item }}` で要素を表示する
  - `:key="index"`は `v-for`を使う上で必要と怒られるから入れる(今回利用しないので `"item"`でもOK)

#### 2. ボタンを押したら、選択されたボタンの色が変わって欲しい

- script内の `data`で選択されたボタン(ここでは `selectedItem`)の初期状態を定義

設定しないとエラーになります。
`selectedIndex`については、下で説明する演算子（ `:color="selectedItem && index === selectedIndex ? 'warning' : null"`）の中でアクセスする際には必ず値が入っている（右辺が真の場合は値がある）のでエラーが出ない。右辺と左辺を入れ替えるとエラーが出るので、 `selectedIndex`も定義しておくほうが無難...。

- template `v-btn`内の`@click=`でクリックイベントを用意
  - クリックイベント内 `selectedItem = item;`でそのボタンの要素を `selectedItem`変数に入れる
  - `selectedIndex = index;`でその要素のインデックスを `selectedIndex`変数に入れる

- `v-btn` 要素が持っている属性 `color`に `v-bind`で三項演算子を入れる
    - `:color="selectedItem && index === selectedIndex ? 'warning' : null"`
    - もし何かしらのボタンが押されており、かつその選択されたアイテムのインデックスがこのボタン要素のインデックスと同一なら、色を `warning`系にする、それ以外は色つけしない

#### 3. 押されたボタンのキー（配列内の文字列）を取得したい

- `script`内で `this.selectedItem`に値が入る（取得タイミングには注意！押されなければ `null`のまま）

------

### 余談

#### `v-for`いろいろ

- 公式：[List Rendering -- Vue.js](https://vuejs.org/v2/guide/list.html)

**配列型**

- `v-for="item in items"` → 配列内の要素そのものだけ取る
- `v-for="(item, index) in items` → 上で使ったやつ。インデックスが取れる

**辞書型**

- `v-for="item in objectItems` → 辞書データ内の値部分だけ取る
- `v-for="(item, name) in objectItems` → 辞書データ内の値部分( `item`)とキー部分( `name`)を取る
    - 値が先なので注意
- `v-for="(item, name, index) in objectItems` → もうわかる


#### `v-btn`に `block`属性を追加すると、ボタンの一つ一つが横の幅を全て取るので、ボタンが縦に並ぶ

イメージとしては、`select`で選べるオプション要素が常に開いているような、よくあるデザインで表示できる！

```vuejs:template(HTML)部分
<v-btn
    block
    v-for="(item, index) of items"
    :key="item"
    :color="selectedTemplate && index === selectedIndex ? 'warning' : null"
    @click="
        selectedTemplate = item;
        selectedIndex = index;
    "
    >
    {{ item }}
</v-btn>
```

##### 色を選ぶ際の三項演算子は、Boolean型のデータだともっとシンプルになる

- 例えば「選択されていないボタンは `text`属性で背景をなくす」なら、下のような式になる
    - `:text="!selectedItem || index !== selectedIndex"`

演算を長くしたかったので無理やり上記のような式にしましたが、この式は厳密にはよくないです。右辺が真の場合は左辺も必ず真なので、 `!selectedItem ||` は不要と言えるからです。ただし、右辺をなくす場合は `data`で `selectedIndex`を定義してやらなければエラーが出ます（右辺が偽の場合は必ず `selectedIndex`に値があるため、上の式ではエラーにならない）。ちなみに右辺だけだと選択したボタンも背景が消えますので判定式の意味がなくなる。

##### `warning`系のカラー？

マテリアルデザインではおなじみのsuccessやerrorなどアラート用要素に、既に組み込みクラスとしての色の組み合わせが用意されています。
Vuetifyでは、success, info, warning, errorの四つのタイプが用意されているようです。

- 公式： [Alert component -- Vuetify.js](https://vuetifyjs.com/en/components/alerts/)

もちろん、他の色のクラスも利用できます。

- 公式：[Material color palette -- Vuetify.js](https://vuetifyjs.com/ja/styles/colors/#material-colors)

### 参考にしたサイト

- [Vuetify.js: How do I change color of v-btn component when clicked - Stack Overflow](https://stackoverflow.com/questions/56831216/vuetify-js-how-do-i-change-color-of-v-btn-component-when-clicked)
- [Use Vuetify v-btn with loader inside a v-for loop - Stack Overflow](https://stackoverflow.com/questions/57617579/use-vuetify-v-btn-with-loader-inside-a-v-for-loop)