Rails 2.0 で in_place_editor_field を使う
Chad Fowler 著「Rails レシピ」の「レシピ 01 同一画面でのフォーム編集」で取り上げられているインプレース編集機能。これは、あるレコードを変更するときに、変更画面に画面遷移することなく、その場で変更ができるという便利な機能だ。もっとも「Ajax らしい」機能の一つではないだろうか?
Rails 1.2 までは標準で in_place_editor_field というメソッドがあって、ビューの中で次のように使うことができた。
<%= javascript_include_tag :defaults %> <%= in_place_editor_field :book, :name %>
この例では Book というモデルの name 属性をこの場で編集することを想定している。
Rails 2.0 になって、in_place_editor_field はプラグインに追い出された。次のようにインストールする。
% cd RAILS_ROOT % ruby script/plugin install in_place_editing
さてここで大きな問題がひとつある。Rails 2.0 からは、デフォルトで POST リクエストに対しては認証トークンのチェックを行うようになった。これは CSRF 対策なのだが、どうやら in_place_editing プラグインはこれに対応していないようだ。そこで、InPlaceMacrosHelper#in_place_editor を書き換えて、認証トークンを送るようにしてみた。下のコードをconfig/environment.rb の一番下にコピ&ペーストすれば動くようになるはずだ。あるいは、プラグイン自体を改造してみてもよいだろう。
module InPlaceMacrosHelper def in_place_editor(field_id, options = {}) function = "new Ajax.InPlaceEditor(" function << "'#{field_id}', " function << "'#{url_for(options[:url])}'" js_options = {} js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text] js_options['okText'] = %('#{options[:save_text]}') if options[:save_text] js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text] js_options['savingText'] = %('#{options[:saving_text]}') if options[:saving_text] js_options['rows'] = options[:rows] if options[:rows] js_options['cols'] = options[:cols] if options[:cols] js_options['size'] = options[:size] if options[:size] js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control] js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url] js_options['ajaxOptions'] = options[:options] if options[:options] js_options['evalScripts'] = options[:script] if options[:script] callback = if options[:with] "function(form) { var params = #{options[:with]};" else "function(form) { var params = Form.serialize(form); " end callback += "params = (params ? params + '&' : '') + '#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}'); return params;}" js_options['callback'] = callback js_options['clickToEditText'] = %('#{options[:click_to_edit_text]}') if options[:click_to_edit_text] function << (', ' + options_for_javascript(js_options)) unless js_options.empty? function << ')' javascript_tag(function) end end