セレクトボックスの面倒な実装をVue.jsで楽をしようとした話

## はじめに
初めまして、[ｋ．ｓ．ロジャース](https://www.wantedly.com/companies/ks-rogers)の西谷です。

最近は引き続き、PHPを使ったWebシステム開発を中心に担当させて頂いております。
その中で、jQueryで実装していたところ、**Vue.jsを使うと楽できるよ！** との話を聞きました。
普段の開発ではReactを利用しておりますが、弊社のHPはNuxt.jsで構築しています。
そのため、この機会にVue.jsを勉強しようと思いました。

間違い・助言等があればコメントにてお知らせいただけたらと思います。

## 作るもの
アイテムを追加出来るリストです。
機能は以下の通りで、会社の選択によって以降の選択肢の表示/非表示が変わります。

- AddItem: ボタンを1行(3つ)追加
- DeleteItem: ボタンを1行(3つ)削除
- 会社の選択肢
  - A社はアイテムの選択肢を表示
  - B社は場所とアイテムの選択肢を表示
- 選択肢は文字列検索が必要

![pIi10ye9QxT1rs3xn5rY62nEzXR6kyCuekCmZqNRQvBM2Jm6vLweGgvt1eTbQ8zxk5OE4GJL6r-1EV3Yj0JuCRN4-4nFzwBLBS8IftWPAGp27O3hG5pYRsk_QNBzNdV61uPw7AkQ.png](https://qiita-image-store.s3.amazonaws.com/0/274674/b8f54556-a36d-b1d1-3b94-14a004b87922.png)


セレクトの追加削除だけでしたら、そこまで難しくないと思いますが、
アイテム数が多くなったときを想定して、検索機能や複数選択機能を追加すると複雑になります。
検索機能を実現するために`bootstrap-select`, `bootstrap-multiselect`をよく利用させて頂いていますが、jQueryで丸ごとcloneしてしまうと上手くいかないことが多く、唸りながら修正することが多々あります。

普段フロントをあまり書かない人からすると、辛いものがあるので今回のVue.jsで楽になれたらと思います。

## 実装
今回はお手軽なCDN版を利用しました。
また、select機能の拡張に[Vue-multiselect](https://vue-multiselect.js.org)を利用しています。

コンポーネントは画像の2つに分けました。
1セットのアイテムを纏めたコンポーネントを親のコンポーネントで追加と削除を行う感じです。

![gN6UTdk009tM2mA7OGjL0RvChF9NI4YnlVDyqxrZgEHn1GfVlgp-QN_aVwcuEUKTSLzLbV0VbOn53m5UrwfWra2kqpPP4-J4UpCUZlTc2XXKXhh-Vjce9qnVQxCOlkegi1otsR6U.png](https://qiita-image-store.s3.amazonaws.com/0/274674/c84d59ef-0095-fc1a-32ef-01f834c6752b.png)


アイテムのコンポーネント(青枠)のコードは次のようになりました。
選択肢切り替えによる、表示/非表示の制御は全てここで行うようにします。

```js
// vue-multiselectの登録
Vue.component('vue-multiselect', window.VueMultiselect.default);

const ItemGroup = {
   data() {
       return {
           "company": {"id": 0, "label": "会社"},
           "place": {"id": 0, "label": "場所1"},
           "item": {"id": 0, "label": "アイテムA"},
           "companyOptions": [
               {"id": 0, "label": "会社"},
               {"id": 1, "label": "A社"},
               {"id": 2, "label": "B社"}
           ],
           "placeOptions": [
               {"id": 0, "label": "場所1"},
               {"id": 1, "label": "場所2"},
               {"id": 2, "label": "場所3"}
           ],
           "itemOptions": [
               {"id": 0, "label": "アイテム1"},
               {"id": 1, "label": "アイテム2"},
               {"id": 2, "label": "アイテム3"}
           ],
       }
   },
   template: `
<div>
   <div style="float:left; width:33%;">
       <vue-multiselect v-model="company" :options="companyOptions" track-by="id" label="label"></vue-multiselect>
   </div>
   <div style="float:left; width:33%;">
       <vue-multiselect v-model="place" :options="placeOptions" track-by="id" label="label" v-if="company != null && company.id == 2"></vue-multiselect>
   </div>
   <div style="float:left; width:33%;">
       <vue-multiselect v-model="item" :options="itemOptions" track-by="id" label="label" v-if="company != null && company.id != 0"></vue-multiselect>
   </div>
   <div style="clear:left"></div>
</div>
`
};

```

次は親となる赤枠のコードです。
こちらは一覧の表示と追加削除を行っています。

```js
const ItemList = {
   components: {
       'item-group': ItemGroup
   },
   data() {
       return {
           itemList: []
       }
   },
   methods: {
       addItem() {
           this.itemList.push({});
       },
       deleteItem() {
           this.itemList.pop();
       },
   },

   template: `
   <div style="width: 100%; margin-bottom: 10px;">
       <button class="btn btn-default" type="button" v-on:click="addItem" name="add-major">Add Item</button>   
       <button class="btn btn-danger" type="button" v-on:click="deleteItem" name="delete-major">Delete Item</button>
       <item-group style="margin-top:5px;" v-for="item in itemList" v-bind="item"></item-group>
   </div>
   `
};
```

後はインスタンスを作成するだけで動作します。

```js
new Vue({
   el: '#items',
   components: {
       'item-list': ItemList
   }
});
```

```html
<div id="item-list">
   <item-list></item-list>
</div>
```

## 詰まったところ
当初は`bootstrap-select`をそのまま利用しており、かなり苦戦しました。
[先人達の解決法](https://github.com/vuejs/Discussion/issues/397)も参考にさせて頂きましたが、複雑な操作をしていくうちに対応が困難になりました。

手詰まりの中で、`Vue-multiselect`を試したところ、
selectのリフレッシュ等を考えなくても動くようになり、直感的に実装することが出来るようになりました。

## おわりに
今回は以前jQueryで書いていたものをVue.jsで実装しました。
jQueryに比べ、機能がコンポーネント毎に分かれて分かりやすいと感じました。
タイトルの楽になるかと言うと、実装に慣れる必要があるかなと思いました。
しかし、機能拡張時や後で見返したときには間違いなく楽になると感じているため、引き続き勉強していくつもりです。
### Wantedlyでもブログ投稿してます
Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers