Selectボックスの監視
Selectボックスで選択を行うとその内容によって他のSelectボックスの内容を更新するというのをobserve_fieldヘルパーを使って実装してみました。
これは、フィールドの値変更を監視、変更が行われるとJavascriptの非同期通信API(Ajax)を使って他方のフィールドのデータをサーバからとりこみ表示するというものです。
今回カテゴリーが選択されるとサブカテゴリーのSelectボックスが更新されるというものを例として実装しました。
prototype.jsのinclude
前提としてprototype.jsが取り込まれるようにしておきます。Viewのはじめのほう(通常headタグのところ)に以下のようにいれておくと、prototypeとscript.ac.ulo.us関連のスクリプトが読みこまれるようになります(もちろんこれらのファイルがサーバの適当な場所の配置されている必要があります)
<%= javascript_include_tag :defaults %>
Viewの作成
selectボックス2つ(監視されるほうと、更新されるほう)をつくり、observe_fieldで監視されるようviewを実装します。
<% form_for (:product, @product) do |f| %> <table> ....省略.... <td> <%= f.select :category_id, @categories.collect { |e| [e.name, e.id] } %> </td> <td > <%= f.select :sub_category_id, @sub_categories.collect { |e| [e.name, e.id] } %> </td> <%= observe_field :product_category_id , :update => :product_sub_category_id, :url => {:action => 'sub_categories'}, :on => "change", :with => :product_category_id %> .... <% end %>
-
- obverve_fieldの1つ目のパラメータは、監視されるフィールドのタグidです。form_forの名前が:product, selectボックスの名前が:category_idとしてしているのでその監視されるselectボックスのidは「product_category_id」となります。
- 「:update =>」 で更新されるselectボックスのタグidを指定します。
- 「:url =>」 で実行するアクションを指定します。
- 「:on => "change"」 でchangeイベントを監視することを指定しています。
- 「:with =>」 では、値をパラメータとして送信するタグのidを指定しています。ここで指定するとが、コントローラ内のparamメソッドでの取得ができます。
observe_fieldヘルパー挿入の結果として、以下のようなAjaxのにUpdaterメソッドをイベントに登録するJavascriptが生成されます。
new Form.Element.EventObserver( 'product_category_id', function(element, value) { new Ajax.Updater('product_sub_category_id', '/admin/product/update_sub_categories', {asynchronous:true, evalScripts:true, parameters:'product_category_id=' + value + '&authenticity_token=' + encodeURIComponent('df4b585074f127bf0c4930496732486b0416dd69')})}, 'change')
更新先フィールドのためのView
更新先のSelectの内容を更新するためのViewを作成します。option_for_selectをつかってOptionタグがレンダリングされるようにします。今回アクション名が「sub_categories」なのでviewのファイル名は「sub_categries.html.erb」となります。
<%= options_for_select @sub_categories.collect { |e| [e.name, e.id] } %>
アクションを作成
def sub_categories @sub_categories = SubCategory.find_all_by_category_id(params[:product_category_id]) render :layout => false end
renderメソッドを「:layout」オプションパラメータにfalseを指定して、レイアウト部分が含まれないよう(対象のviewのみがレンダリングされるよう)にします