Rails: HotwireCombobox gemが素晴らしすぎるという話(翻訳)
hotwireと相性バツグンの「コンボボックス(Web向けのドロップダウンボックスやセレクトボックスの一種で、項目を手入力すると他のオプションをフィルタできる)」の書き方を知るという大きな幸運を引き当てたので、今週の記事の執筆予定を変更することにしました。その後でたまたま目にしたRuby Weeklyで、同じことを既にやっている人がいることを知りました。
ちなみにその人は作者のJose Fariasで、これをHotwireComboboxと呼んでいます。ドキュメントページにはさまざまなデモが掲載されているので、皆さんもぜひお試しください!
このHotwireCombobox gemの最も素晴らしい点は(これはRails 7のJavaScriptがimport mapsに移行したことで私にとって最も嬉しい点でもあります)、フロントエンドのアセットと仲良く共存できることです。つまり、今後バージョンが上がってもバックエンドとフロントエンドの同期が取れなくなるリスクがありません。
実際、HotwireCombobox gemのセットアップは最小限で済むので、 私のアプリで使われていたf.collection_select
ボックスをf.combobox
に置き換えたときの全変更内容を本記事で公開することにします。
🔗 インストール方法
Gemfile
に以下を追加してからbundle
コマンドを実行します。
gem "hotwire_combobox"
アプリでstylesheet_link_tag
が書かれている場所(おそらくレイアウトの<head>
タグにあります)を探して、その上の行にcombobox_style_tag
をコピペします。私の場合は以下のようになりました。
<%= combobox_style_tag %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
🔗 to_combobox_display
をモデルに追加する
私はActive Recordのモデルにビューがらみの不要なものを追加するのは嫌いなのですが、この機能をなるべく手軽に使えるようにしたかったので、自分のEquipment
モデルに以下を追加して、コンボボックスのリストにtitle
属性を表示するようにしました。
class Equipment < ApplicationRecord
#...
def to_combobox_display
title
end
end
🔗 select
入力を差し替える
続いて、以下のcollection_select
をすべて置き換えました。
<%= f.collection_select :equipment_id, Equipment.all.order(:title), :id, :title, include_blank: true %>
上をf.combobox
で以下のように書き換えます。
<%= f.combobox :equipment_id, Equipment.all.order(:title), include_blank: true %>
原注
2024/03/29時点では、include_blank: true
だけでは動かなかった(#125)ため、自分のカスタムTailwindFormBuilder
でパッチを当てました。
🔗 コンボボックスの外観をカスタマイズする
このコンボボックスで最も手こずったのは、他のカスタムフォーム入力と外観を同じにすることでした。私はTailwindを使っているので、@layer base {}
の内部で必要なものを上書きするために、このHotwireCombobox gemが公開しているCSSクラスや変数を対象としました。
以下は、Tailwindを使っていない方には意味不明に見えるかもしれません。
/* hotwirecombobox */
.hw-combobox__main__wrapper {
@apply mt-1 rounded-md shadow-sm max-w-screen-md;
}
.hw-combobox__main__wrapper input:focus {
@apply ring-0;
}
.hw-combobox__dialog__input {
@apply rounded-lg focus:ring-0 focus:border-accent ;
}
:root {
--hw-focus-color: rgb(var(--accent));
--hw-border-width--thick: 1px;
--hw-combobox-width: 100%;
--hw-combobox-width--multiple: 100%;
}
🔗 以上でおしまいです!
上に挙げた私のスニペットに「ないもの」にご注目ください。import mapのコンフィグもなければ、StimulusコントローラをJavaScriptで登録するステップもありません。すべてこのgemが独自に処理してくれます。
私の経験では、この種のUIを作るのはいつも大変だったので、このように簡単に追加できて、アプリの既存のフロントエンドともうまく連携できるコンポーネントが使えるのは、ひたすら素晴らしいことです。
Jose、ありがとう!💜
概要
原著者の許諾を得て翻訳・公開いたします。