FormBuilderに`datalist`メソッドが追加され、ブラウザオートコンプリートの実装が簡潔に
FormBuilderにf.datalistメソッドが追加され、HTMLの<datalist>要素をモデルと連携した形で簡単に生成できるようになりました。これにより、テキストフィールドとデータリストのID紐付けをフォームビルダーの命名規則に沿って一貫して記述できます。
背景
既存のdatalist_tagヘルパーは<datalist>要素を生成できましたが、FormBuilderと統合されていないため、IDの生成と管理を手動で行う必要がありました。FormBuilderはモデル名とフィールド名からIDを自動導出する仕組みを持っていますが、datalistはその恩恵を受けられず、text_fieldのlist:オプションとの整合性をビュー側で意識して記述する必要がありました。
今回のPR #57318は、既存のdatalist_tagをFormBuilderでラップするf.datalistを追加することで、この手間を解消しています。
技術的な変更
FormBuilderのf.datalistは、内部でfield_id(method, "datalist")を呼び出してIDを導出し、そのIDでdatalist_tagを呼び出します。モデルpostのフィールド:countryに対してはpost_country_datalistというIDが自動生成されます。
追加されたメソッド:
def datalist(method, choices = nil, html_options = {})
@template.datalist_tag(field_id(method, "datalist"), choices, html_options)
end
このメソッドを使ったビューの記述は次のようになります:
<%= form_with model: @post do |f| %>
<%= f.text_field :country, list: f.field_id(:country, :datalist) %>
<%= f.datalist :country, ["Argentina", "Brazil", "Chile"] %>
<% end %>
上記は以下のHTMLを生成します:
<input list="post_country_datalist" type="text"
name="post[country]" id="post_country" />
<datalist id="post_country_datalist">
<option value="Argentina">Argentina</option>
<option value="Brazil">Brazil</option>
<option value="Chile">Chile</option>
</datalist>
choicesには文字列の配列だけでなく、[ラベル, 値]形式のネストした配列も渡せます。["Argentina", "AR"]のように記述すると、<option value="AR">Argentina</option>が生成されます。これは既存のdatalist_tagの動作をそのまま継承したものです。
設計判断
field_idメソッドを用いたIDの自動導出が採用された点が重要です。f.datalistのIDと、対応するtext_fieldのlist:属性に渡す値を、どちらもf.field_id(:country, :datalist)という同一の呼び出しで得られる設計になっています。これにより、ID文字列をハードコードすることなく、フォームビルダーの命名規則が両者の紐付けを保証します。
また、実装はわずか1行の委譲であり、datalist_tagの既存ロジックを再実装していません。choicesやhtml_optionsはそのまま透過的に渡されるため、ラベル・値ペアの指定や任意のHTML属性の付与といったdatalist_tagの機能がすべてf.datalistでも利用できます。
まとめ
f.datalistの追加は、FormBuilderのID導出規則を活用することで、<datalist>とテキストフィールドの結びつきをフレームワーク側で管理できるようにした変更です。実装の委譲構造がシンプルであるため、datalist_tagの既存機能との一貫性が保たれており、既存コードへの影響もありません。