4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue Selectを導入して、いろいろカスタマイズしてみる

Last updated at Posted at 2022-07-31

はじめに

業務でVue Selectを利用する機会があったので、諸々のカスタマイズについての備忘録。
とても便利なライブラリだが、細かな情報を探すのに苦労したのでここに残しておく。

(間違いなどあれば、コメントにてご指摘いただければ幸いです。)

Vue Selectとは

Vue.jsで利用可能なセレクトボックスコンポーネント。
公式サイト 曰く

Everything you wish the HTML element could do, wrapped up into a lightweight, extensible Vue component.
(HTMLの要素にできることをすべて、軽量で拡張性の高いVueコンポーネントにまとめました。)

この記載通りで、とても手軽にセレクトボックスを実装できる。
さらに、選択肢に対する前方一致の絞り込みなどの機能も利用可能な優秀なライブラリ。

筆者環境

Vue.js: 2.6
Vue Select: 3.1
(Laravel: 8.1上で利用)

How to Use

こちらも公式サイトが分かりやすい。
ざっくりnpmの場合を記載しておくと、
 ①npm installを実行

npm install vue-select

 ②コンポーネントとして登録

app.js
import Vue from 'vue'
import vSelect from 'vue-select' 

Vue.component('v-select', vSelect) 

以上で利用準備は完了。あとは自分の使いたいvueファイルで<v-select>を利用するのみ。

さまざまなカスタマイズを試してみる

 以下コードはSFCとして記載。

基本

選択肢べた書きパターン。
タグの中でv-bind:options="選択肢の配列"を設定する。

Sample.vue
<template>
    <v-select
        v-bind:options="['small', 'medium', 'large']"
    >
    </v-select>
</template>

image.png

テキスト入力で、前方一致で選択肢絞り込みもしてくれる。
image.png

配列をdata()に指定する

Vueを使っていてデータをハードコーディングすることは少ないと思うので、dataの値を反映してみる。

Sample.vue
<template>
    <v-select
        v-bind:options="sizes"
    >
    </v-select>
</template>

<script>
export default {
    data() {
        return{
            sizes: ['small', 'medium', 'large'],
        }
    },
}
</script>

image.png
選択するとこのような感じ。(中途半端にCSSが付いているのはスルーいただきたい…)
image.png

選択後にボックス内に表示される'×'を消す

clearableをfalseで指定。

Sample.vue
<template>
    <v-select
        v-bind:options="sizes"
        v-bind:clearable="false"
    >
    </v-select>
</template>

image.png

オブジェクトの配列を選択肢として指定する

画面表示したいプロパティをlabelに設定する。

Sample.vue
<template>
    <div>
        <v-select
            v-bind:options="drinks"
            label="name"
        >
        </v-select>
    </div>
</template>

<script>
export default {
    data() {
        return{
            drinks: [
                {
                    name: 'coffee',
                    price: 150,
                },
                {
                    name: 'tea',
                    price: 140,
                },
                {
                    name: 'milk',
                    price: 120,
                },
                {
                    name: 'water',
                    price: 100,
                },

                ]
        }
    },
}
</script>

image.png

オブジェクトのプロパティから独自の選択肢を作る

<template>タグを用意し、slotでタグ内のoptionと紐づける。
記載した内容はリスト内の<li>の中へ反映される。

Sample.vue
<template>
    <v-select
        v-bind:options="drinks"
    >
        <template v-slot:option="drink" >
            <span>{{drink.name}} : {{drink.price}} yen</span>
        </template>
    </v-select>
</template>

image.png

選択した内容をデータへ反映する

v-modelを利用して、dataへ双方向データバインディングを実現する。
処理の流れは以下の通り。
①ユーザーが選択肢を選択する
②選択した内容がdataのselectedDrinkの値に反映され、選択した値となる
③変更されたselectedDrinkの内容がpタグに反映される

Sample.vue
<template>
    <div>
        <v-select
            v-bind:options="drinks"
            label="name"
            v-model="selectedDrink"
        >
        </v-select>
        <p>You selected: {{selectedDrink.name}} : {{selectedDrink.price}}yen</p>
    </div>
</template>

<script>
export default {
    data() {
        return{
            drinks: [], // 省略
            selectedDrink: {
                name: '',
                price: ''
            },
        }
    },
}
</script>

選択前
image.png

選択後
image.png

選択肢がない場合のメッセージを指定する

こちらも<template>とslotで実現する。
デフォルトのメッセージが英語なので、ぜひ設定しておきたい。

Sample.vue
<template>
    <v-select
        v-bind:options="sizes"
    >
        <template v-slot:no-options>
            選択肢が存在しません
        </template>
    </v-select>
    </v-select>
</template>

image.png

Vue Selectのパーツに任意のCSSを適用する

よく使うだろう属性はCSS valiablesとして提供されているので、そちらを使う。
上記で対応できない場合はクラス指定でCSSを適用する。
image.png
上記キャプチャの通り、各パーツに特定のクラスが付与されているので、
対象パーツのクラスを確認してCSSを指定する。

※vue selectに限らないが、SFCでstyleをscopedで記載している場合、子孫コンポーネントへのCSS付与がうまくできないことがある。
その場合はクラス名の前に ::v-deep を記載すると成功する。(>>>や/deep/が使えずに数時間費やした人)

Sample.vue
<template>
    <v-select
        v-bind:options="sizes"
        label="name"
    >
    </v-select>
</template>

<script>
    // 省略
</script>

<style lang="scss">
:root {
    // 公式提供のCSS Variablesを利用
    --vs-font-size: 14px;
    --vs-dropdown-max-height: 100px;
}

.v-select {
    background-color: lemonchiffon;

    .vs__selected-options {
        font-size: 18px;
    }
}
</style>

image.png
v-selectクラスに付与した背景色と、変数で指定したmax-heightが効いている。

image.png
vs__selected-optionsに指定したfont-sizeも適用されている。

イベントに応じて処理を行う

Vue Selectがいくつかのイベントをemitで提供してくれるので、それをv-onで拾う。

以下では、この通りの処理を行う。
 ①リストを表示したタイミングで、classを付与。background-colorを変更。
 ②選択肢が選ばれたタイミングでalert()
 ③リストが閉じられたタイミングで、classを外す。background-colorを戻す。

Sample.vue
<template>
    <div>
        <v-select
            v-bind:options="drinks"
            label="name"
            v-bind:class="{'selecting': isActive}"
            v-on:input="changeDrink"
            v-on:open="changeListStatus"
            v-on:close="changeListStatus"
        >
        </v-select>
    </div>
</template>

<script>
export default {
    data() {
        return{
            drinks: [],  // 省略
            isActive: false,
        }
    },
    methods: {
        changeDrink: function() {
            alert('drink selected!');
        },
        changeListStatus: function() {
            this.isActive = !this.isActive;
        },
    }
}
</script>

<style lang="scss">
.selecting {
    background-color: lightcoral;
}
</style>

①エリアをクリック(リストをopen)
image.png

②選択肢をクリック
image.png

③OKをクリック(リストをclose)
image.png

終わりに

ここに書くことのできたカスタマイズはほんの一部で、他にも様々なpropsやeventsが提供されている。
導入・実装もとても簡単なので、Vue.jsでセレクトボックスを利用する場合は、ぜひVue Selectの利用を検討してみてほしい。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?