ヘルパメソッドremote_parentを定義する。

モーダルウィンドウをiframe=>trueで利用していると、モーダルウィンドウから背景の親ウィンドウを更新したい状況っていうのがよくある。ヘルパメソッドのremote_functionを使いたいのだが、iframeの親ウィンドウに対する操作なので、うまく機能しない...。 仕方なく、以下のようにJavaScriptを直接書いていた。

  • オレンジ色の部分が、remote_functionでは指定できない(と思う)ので、JavaScriptで書くことになる。
<%= javascript_tag(
      "new parent.Ajax.Updater('list_update', 
                               '/csvs/list_update?sort_direction=desc&sort_field=created_at', 
                               {asynchronous:true, 
                                evalScripts:true, 
                                parameters:parent.Form.serialize('list_params')} );" %>


しかし、JavaScriptには慣れていないので、今イチ使い難い...。そうだ!remote_parentを作ろう!と、思い立った。上と同じ結果を、以下のように書けるようにしたい。

<%= javascript_tag(
      remote_parent :update=>'list_update', 
                    :submit=>'list_params', 
                    :url=>{:action=>:list_update, 
                           :sort_field=>'created_at', 
                           :sort_direction=>'desc'}) %>
  • これなら見慣れた書式で、理解し易い。
  • urlの部分は、以下のように書き換えてもOK。
<%= javascript_tag(
      remote_parent :update=>'list_update', 
                    :submit=>'list_params', 
                    :url=>'/csvs/list_update?sort_direction=desc&sort_field=created_at' ) %>


作るといっても、以前in_place_editorを拡張した時と同じRailsソースのコピー作戦だ。(手抜きです。きっと、もっと立派な解決法があると思う...。)

  • remote_functionのソースをapp/helpers/application_helper.rbへコピーする。ソースは、自分のLocomotive環境では、以下のようなディレクトリ階層奥深くに眠っている。
/Applications/Locomotive2/Bundles/standardRailsSept2006.locobundle/i386/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/prototype_helper.rb
  • options_for_ajaxのソースもコピーした。(remote_functionから呼び出されるメソッドで、ここにもparentを付加したい部分がある。)
app/helpers/application_helper.rb
ヘルパー
  • メソッド名にparentを含めて微妙に変更
  • 必要な箇所にparent.を追記した。
  • JavaScriptメソッドを連続したい時に不便だったので、メソッドの最後は「;」セミコロンで終わるようにした。
module ApplicationHelper
...(途中省略)...
  def remote_parent(options)
        javascript_options = options_for_parent(options)

        update = ''
        if options[:update] and options[:update].is_a?Hash
          update  = []
          update << "success:'#{options[:update][:success]}'" if options[:update][:success]
          update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
          update  = '{' + update.join(',') + '}'
        elsif options[:update]
          update << "'#{options[:update]}'"
        end

        function = update.empty? ? 
          "new parent.Ajax.Request(" :
          "new parent.Ajax.Updater(#{update}, "

        url_options = options[:url]
        url_options = url_options.merge(:escape => false) if url_options.is_a? Hash
        function << "'#{url_for(url_options)}'"
        function << ", #{javascript_options})"

        function = "#{options[:before]}; #{function}" if options[:before]
        function = "#{function}; #{options[:after]}"  if options[:after]
        function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
        function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]

        return function + ';'
  end

  def options_for_parent(options)
        js_options = build_callbacks(options)

        js_options['asynchronous'] = options[:type] != :synchronous
        js_options['method']       = method_option_to_s(options[:method]) if options[:method]
        js_options['insertion']    = "Insertion.#{options[:position].to_s.camelize}" if options[:position]
        js_options['evalScripts']  = options[:script].nil? || options[:script]

        if options[:form]
          js_options['parameters'] = 'parent.Form.serialize(this)'
        elsif options[:submit]
          js_options['parameters'] = "parent.Form.serialize('#{options[:submit]}')"
        elsif options[:with]
          js_options['parameters'] = options[:with]
        end

        options_for_javascript(js_options)
  end
end


ちょっとした変更なんだが、自分にとっては嬉しいメソッドが出来上がった!