検索可能なプルダウンリストで、二つのフォームを連動させる。

auto_completeの使い方が分かってきたところで、最初の銀行支店をスマートに選択する課題に戻ってみる。

  • まず、銀行と支店を検索するので、text_fieldを二つ用意した。
  • そうすると、二つのtext_fieldはお互いに連携している必要がある。以下のような連携を求めてみた。
    • 銀行は、支店とは無関係に選択できる。支店は。銀行に紐付くものだけに絞り込んで選択するようにしたい。
    • 例えば、「0001 みずほ銀行」を選択したら、支店を検索する時には、みずほ銀行の支店だけに絞り込んで検索するようにしたい。
    • 既に銀行と支店が決定されている状況で、銀行を選択し直した時は、支店のtext_fieldは一度空欄にしておきたい。
    • 銀行を決定する前に支店を選択しようとした場合は、「銀行を先に選択してください。」とメッセージを表示したい。

以下のような銀行テーブル、支店テーブルを想定して...

id name kana
1 みずほ ミズホ
5 三菱東京UFJ ミツビシトウキョウUFJ
9 三井住友 ミツイスミトモ
... ... ...
id code name kana bank_id
1001 1 本店 ホンテン 1
1005 5 丸之内支店 マルノウチ 1
... ... ... ... ...

以上のことを踏まえて、以下のようなコードを考えてみた。

<%# ビュー %>
<%= text_field_with_auto_complete :bank, :kana %>
<%= hidden_field :payee_account, :bank_id %>

<%= text_field_with_auto_complete : branch, :kana %>
<%= hidden_field :payee_account, : branch_id %>
# コントローラー:銀行の検索
def auto_complete_for_bank_kana
  find_options = { 
    :conditions => [ "LOWER(kana) LIKE ?", '%' + params[:bank][:kana].downcase + '%' ], 
    :order => "kana ASC",
    :limit => 10 }
  @items = Bank.find(:all, find_options)
  render :partial => 'auto_complete_for_bank_kana'
end

# コントローラー:支店の検索
def auto_complete_for_branch_kana
  Branch.with_scope(:find=>{:conditions=>[ "bank_id=?", params[:payee_account][:bank_id] ]}) do
    find_options = { 
      :conditions => [ "LOWER(kana) LIKE ?", '%' + params[:branch][:kana].downcase + '%' ], 
      :order => "kana ASC",
      :limit => 10 }
    @items = Branch.find(:all, find_options)
    render :partial => 'auto_complete_for_branch_kana'
  end
end

ここまで書いて、疑問発生!支店を検索する時に、現在選択済みの銀行コードはどうやって取得すれば良いのだろう?

  • text_field_with_auto_complete :branch, :kanaと書いた時は、text_fieldに文字列が入力されると、コントローラーのauto_complete_for_branch_kanaが呼び出される。
  • コントローラーでは、params[:branch][:kana]で、その時入力された文字列を取得できる。
  • きっとtext_field_with_auto_completeのところで、欲しい銀行コードを送信しておく必要があるはず...。
  • とりあえずここでは、仮にparams[:payee_account][:bank_id]でその銀行コードを取得できると考えて、書いておいた。

auto_completeで、欲しい情報を追加する

調査と試行錯誤の結果、:withオプションを利用して、以下のように設定すれば、銀行コードも一緒にパラメーターとして送信できるようだ。

<%= text_field_with_auto_complete :branch, :kana, 
      {}, :with=>"value + '&payee_account[bank_id]=' + $('payee_account_bank_id').value" %>
  • value」は、text_field_with_auto_complete :branch, :kanaに入力された値を送信する。
  • 「'&payee_account[bank_id]='」はparams[:payee_account][:bank_id]のキーの部分。
    • 先頭の「&」はパラメーターの区切り記号。
    • ' 'クォーテーションで囲う必要あり。
  • 「$('payee_account_bank_id').value" 」でid属性を指定して、その値を取り出している。

これで、銀行コードと連携した支店検索が可能になったので、あとは選択リストの表示を制御すれば良い。以下のようにやってみた。

<%# ビュー_auto_complete_for_bank_kana.rhtml %>
<ul>
  <% @items.each do |item| %>
    <li>
      <span connect="payee_account_branch_id"></span>
      <span connect="branch_kana"></span>

      <span connect="payee_account_bank_id"><%= item.id %></span>
      <span class="informal"><%= item.kana %></span>
      <span connect="bank_kana"><%= item.name %></span>
    </li>
  <% end %>
</ul>
<%# ビュー_auto_complete_for_branch_kana.rhtml %>
<ul>
<% @items.each do |item| %>
  <li>
    <span connect="payee_account_branch_id"><%= item.id %></span>
    <span class="informal"><%= item.kana %></span>
    <span connect="branch_kana"><%= item.name %></span>
  </li>
<% end %>

<% if params[:payee_account][:bank_id].blank? %>
  <li>
    <span connect="payee_account_branch_id"></span>
    <span connect="branch_kana"></span>
    <span class="informal">銀行を先に入力してください。</span>
  </li>
<% end %>
</ul>

これで、銀行と支店が連動した検索が可能になったが、一つ問題に気付いた。1件も見つからなかった時に何も表示されないので、キー入力に対して反応がないと勘違いしてしまうのだ。以下のように修正した。

<%# ビュー_auto_complete_for_bank_kana.rhtml %>
<ul>
  <% @items.each do |item| %>
    <li>
      <span connect="payee_account_branch_id"></span>
      <span connect="branch_kana"></span>

      <span connect="payee_account_bank_id"><%= item.id %></span>
      <span class="informal"><%= item.kana %></span>
      <span connect="bank_kana"><%= item.name %></span>
    </li>
  <% end %>

  <% if @items.blank? %>
    <li>
      <span connect="payee_account_branch_id"></span>
      <span connect="branch_kana"></span>

      <span connect="payee_account_bank_id"></span>
      <span connect="bank_kana"></span>
      <span class="informal">銀行が見つかりません。</span>
    </li>
  <% end %>
</ul>
<%# ビュー_auto_complete_for_branch_kana.rhtml %>
<ul>
<% @items.each do |item| %>
  <li>
    <span connect="payee_account_branch_id"><%= item.id %></span>
    <span class="informal"><%= item.kana %></span>
    <span connect="branch_kana"><%= item.name %></span>
  </li>
<% end %>

<% if params[:payee_account][:bank_id].blank? %>
  <li>
    <span connect="payee_account_branch_id"></span>
    <span connect="branch_kana"></span>
    <span class="informal">銀行を先に入力してください。</span>
  </li>
<% elsif @items.blank? %>
  <li>
    <span connect="payee_account_branch_id"></span>
    <span connect="branch_kana"></span>
    <span class="informal">支店が見つかりません。</span>
  </li>
<% end %>
</ul>

これで、検索結果が0件であっても、「銀行が見つかりません。」「支店が見つかりません。」と表示されるので安心できる。

検索で一致した部分を強調表示する

ついでに、上記コードの<%= item.kana %>の部分を以下のように変更すれば、一致した部分が強調表示されたリストが得られる。

<%# ビュー_auto_complete_for_bank_kana.rhtml %>
...途中省略...
<%= highlight(item.kana, params[:bank][:kana]) %>
...途中省略...
<%# ビュー_auto_complete_for_branch_kana.rhtml %>
...途中省略...
<%= highlight(item.kana, params[:branch][:kana]) %>
...途中省略...

これで、こんな表示になる。

ちなみに、auto_complete_result*1のphraseオプション(第3引数)を指定しても、同じように強調表示される。(kanaフィールドだけの表示になってしまうが...。)

render :inline=>"<%= auto_complete_result(@items, :kana, params[:bank][:kana]) %>"

*1:選択リストの内容をhtmlとして返してくれるヘルパメソッド。auto_complete_forによって生成されるメソッドの中で選択リストを描画する時に呼び出されている。(部分テンプレートを利用せずに。)