🗂

Vueコンポーネントの動作を上書きする方法

2021/06/17に公開

オンライン家庭教師マナリンク 開発の Technote です。
今回は Vuetify の VAutocomplete を使用して新しく検索機能を追加したときのネタです。

仕様

新しく作成した検索機能は以下のような仕様でした。

  • 入力に応じて科目などを検索候補として表示
    • ひらがなで入力しても検索候補を出す
    • 検索候補を選択すると専用のページに遷移
  • 検索候補を選択せず検索アイコンをクリックでキーワード検索

demo

VAutocomplete

入力で絞り込めるセレクトのようなコンポーネントです。
https://vuetifyjs.com/ja/components/autocompletes/

サンプルの動作を見て今回の実装に合っていると思ったので使うことにしました。

空になる入力

日本語の検索もできる外部サービスを使用して別途作成したAPIのレスポンスを items に動的に設定することで簡単にある程度の実装ができました。
API側で絞り込みを行ったので no-filter でフロントでの絞り込みはオフにしました(ひらがな入力の際に弾かれてしまうため)

なかなかいい感じに実装できたのですが、検索窓の入力が事あるごとに空になる事がわかりました。

  • 検索でページを遷移した後
  • 検索窓からフォーカス外れたとき
  • ページロード時に query の keyword を search-input に設定して検索窓の入力を復元しようとした後

deleted

キーワード検索の結果ページでは検索キーワードが検索窓に入ったままにしたかったので困りました。

実装を見てみる

https://github.com/vuetifyjs/vuetify/blob/3024044342cdb06bbe63fca92cc41be31017b3bd/packages/vuetify/src/components/VAutocomplete/VAutocomplete.ts

選択されていない場合に入力を null にする setSearch が フォーカスが外れた際に呼ばれる updateSelf などで呼ばれていることがわかりました。

フォーカスが外れても空にならないようにしたい。。

普段は React を使用しているので Vue でコンポーネントを拡張する方法はよく知りませんでしたが、Vuetify の「よくある質問」に欲しかった答えがありました!

https://vuetifyjs.com/ja/getting-started/frequently-asked-questions/#extend-components

Vuetifyコンポーネントを拡張するにはどうすればいいですか?

Vuetifyコンポーネントは、vueで extends オプションを使用することで簡単に拡張できます。

extends オプション

この extends は Vue の ComponentOptions に定義されているので Vuetify 以外でも使用できるはずなのですが、Google検索では使用している例はほとんど見つかりませんでした。

https://github.com/vuejs/vue/blob/c52427b0d2c1d203deea6eb69f2b4b181d56022c/types/options.d.ts#L119

これを使用して以下のように setSearch を override することで空にする動作を止めることができました。

import { VAutocomplete } from 'vuetify/lib'

export default {
  extends: VAutocomplete,
  methods: {
    setSearch() {}
  }
}

extend による拡張

もう少し試してみると extends オプションを使わなくても同様のことができることがわかりました。

import { VAutocomplete } from 'vuetify/lib'

export default VAutocomplete.extend({
  setSearch() {}
})

https://jp.vuejs.org/v2/api/index.html#extends

https://v3.ja.vuejs.org/api/options-composition.html#extends

今回のようにコンポーネントライブラリを使っていてちょっと内部の動作を変えたいときのために覚えておいてもいいかも。

マナリンク Tech Blog

Discussion