Rails3以降にremote_function, link_to_remoteを移植してみる
かつてRails2にはremote_functionや、link_to_remoteと言う神メソッドがあった。
Rails2のAjaxライブラリはPrototype.jsが採用されていた。
しかしRails3以降、Prototype.jsはバイバイして神メソッドは使用することが出来ず、
link_to 'タイトル', URL, :remote => true
という、オプションを渡すことでAjax通信をするように変更された。
DOMオブジェクトを書き換える際は、js.erbファイルをrenderし、さらにそのファイル内でコールバックしたdataを指定したIDに挿入という、プログラミング初心者の私には、血尿レベルの意味不明っぷりであった。
本当に使えないのか...?
jQuery用にマッピングしなおせばいいだけじゃないのか...?
私は立ち上がった。
と思ったので、link_to_remoteのソースコードをjQuery用に書き換えてみたよ。
多分不完全なので、適宜修正が必要かもしれないよ。
Prototype.jsではコールバックに色々利用できたけど、jQuery.ajaxは[success,complete,error]の3種類だけが利用できるみたいだよ。
こんな感じ?
= link_to_remote('title', { update: :catalogs, url: {action: :index, controller: :menu}, submit: :form, success: "alert('success')", complete: "alert('complete')", error: "alert('error')", position: :bottom }, href: "#", style: 'margin:100px')
下記ソースを、app/helpers当たりにぶちこんだよ。
#!/usr/bin/ruby # -*- encoding: UTF-8 -*- module HtmlTagHelper #################################### # link_to_remote # #################################### JS_ESCAPE_MAP = { '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'" } CALLBACKS = Set.new([ :complete, :error ] + (100..599).to_a) if "ruby".encoding_aware? JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' else JS_ESCAPE_MAP["\342\200\250"] = '
' end def escape_javascript(javascript) if javascript result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] } javascript.html_safe? ? result.html_safe : result else '' end end def link_to_remote(name, options = {}, html_options = {}) link_to_function(name, remote_function(options), html_options || options.delete(:html)) end def link_to_function(name, function, html_options={}) onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" href = html_options[:href] || '#' content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) end def remote_function(options={}) javascript_options = options_for_ajax(options) callbacks = build_callbacks(options) update = '' if options[:update] && 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 = "$.ajax({" function << "type: '#{ options[:method] ? options[:method] : 'GET' }'," function << "url: '#{ options[:url] ? url_for(options[:url]) : '#' }'," function << "data : #{ javascript_options }," callbacks.each {|callback,code| function << "#{callback.to_s}: #{code}, " } if callbacks.present? function << "success: function(data) {" function << options[:success] + '; ' if options[:success].present? function << case options[:position] when :top "$('##{options[:update]}').prepend(data); " when :bottom "$('##{options[:update]}').append(data); " when :before "$('##{options[:update]}').before(data); " when :after "$('##{options[:update]}').after(data); " else "$('##{options[:update]}').html(data); " end if options[:update] function << "} });" 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.html_safe end def options_for_ajax(options) js_options={} if options[:submit] js_options['parameters'] = "$('##{options[:submit]} input, ##{options[:submit]} textarea, ##{options[:submit]} select, ##{options[:submit]}').serialize('')" elsif options[:with] js_options['parameters'] = options[:with] else js_options['parameters'] = "$('form').serialize(this)" end if protect_against_forgery? && !options[:form] if js_options['parameters'] js_options['parameters'] << " + '&" else js_options['parameters'] = "'" end js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')" end end def build_callbacks(options) callbacks = {} options.each do |callback, code| if CALLBACKS.include?(callback) callbacks[callback] = "function(request){#{code}}" end end return callbacks end def method_option_to_s(method) (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'" end end