Responds to Parent

In the pursuit of AJAX file uploads, I ran into the pattern of posting a form to a hidden IFRAME returning javascript control back to the parent page to display a progress bar, make visual effects, show a “uploading” graphic or basically do anything than the default blocking of the page until the upload is finished.

Just give me the plugin.

With Rails and RJS, I wanted an easy way to maintain my page update logic without having to worry (too much) about where that code was being executed. This follows the iframe remoting pattern.

Taking the pragmatic approach, we just create an IFRAME in our page to accept the form, target the form to the IFRAME, then in our controller’s form action, we return javascript to be evaluated in the parent window. Easy as that. Here’s the example:

Controller:

  class Test < ActionController::Base
    def main
    end

    def form_action
      # Do stuff with params[:uploaded_file]

      responds_to_parent do
        render :update do |page|
          page << "alert($('stuff').innerHTML)"
        end
      end
    end
  end

main.rhtml:

<html>
    <body>
      <div id="stuff">Here is some stuff</div>

      <form target="frame" action="form_action">
        <input type="file" name="uploaded_file"/>
        <input type="submit"/>
      </form>

      <iframe id='frame' name="frame"></iframe>
    </body>
  </html>

All we did was wrap the render :update block with a responds_to_parent block. Now your RJS update executes in the parent window rather than in the IFRAME.

Tested environments:

  • Safari 2.0.3
  • Firefox 1.5.0
  • IE 6
  • Opera 8.52, 9b2 (Thanks Julio Cesar Ody for the fix!)

Extra:

24 responses

12 12 2007
In-place file upload with Ruby on Rails || Dmytro Shteflyuk’s Home

[...] and it’s implementation as Ruby on Rails plugin here. Example of using: class TestController < ActionController::Base   def upload_action   [...]

16 01 2008
Bhe Bautista

I’m having problem with respond_to_parent plugin in Safari3. The page.replace_html(Element.update()) doesn’t work. It’s fine in Safari 2 but it crashes in Safari 3.

21 01 2008
Richard

Seems to not be working in Safari 3, what is the problem here? Are you planning to upgrade with a fix to this? I get the following error

RJS error: TypeError: Value undefined (result of expression Element.update) is not object.

Many thanks

21 01 2008
streadway

I will try to fix then when time permits, but that may be a while. I’ll happily apply any patches that fix this, so if you would like to investigate a little bit, I know many that will be grateful.

23 01 2008
Yuval Kordov

Hi Sean. Fabulous plugin that has enabled me to do all the client-side form error handling I wanted to do when uploading. However, as stated above, I have also run into problems with Safari 3. I provided a detailed example here. Please let me know if you need any more specific info. Cheers.

13 02 2008
WEBtudinho v. 2.0 - » Upload de arquivos com AJAX em Ruby on Rails

[...] lidar com o formulário no servidor, utilizaremos o plugin responds_to_parent: script/plugin install [...]

20 02 2008
Cid Dennis

Is there a way to access both the parent and the iframe. So for example I could do some work in the iframe and still update the parent at the same time?

Thanks

20 02 2008
streadway

Sorry, but the iframe is just a workaround and was intended to not be used as anything other than to make the browser behave asynchronously. So nope, just the parent is intended to be used. If you wish work with another iframe then include that in the parent frame.

11 03 2008
Rafael Mueller

The “Just give me the plugin.” isnt working, 404 error

Regards

11 03 2008
streadway

Good catch Rafael. The link now heads on over to Google Code.

3 04 2008
25 04 2008
Peter T. Brown

BTW, this tends to fail in Rails 2.0.2 with the error:

/sw/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/active_support/dependencies.rb:376:in `new_constants_in’: You have a nil object when you didn’t expect it! (NoMethodError)
You might have expected an instance of Array.
The error occurred while evaluating nil.empty? from /sw/lib/ruby/gems/1.8/gems/activesupport-2.0.2-/lib/active_support/dependencies.rb:202:in `load_file’

To fix, you’ve got to add to the top of init.rb:

require ‘responds_to_parent’

7 05 2008
A Fresh Cup » Blog Archive » Double Shot #202

[...] Responds to Parent – This may be another bit required to get multi-file uploads working: a way to send AJAX responses back from an IFRAME to its parent. [...]

23 05 2008
Double Shot #202 | Developer Home

[...] Responds to Parent – This may be another bit required to get multi-file uploads working: a way to send AJAX responses back from an IFRAME to its parent. [...]

11 06 2008
17 07 2008
Zone of Mr. Frosti » Blog Archive » Adventures with Rails: Part Quatre

[...] Targeted Announcement editor complete with TinyMCE (the rich-text editor), AJAX file uploads (using responds_to_parent), and Javascript live form validation. The real showcase here is outside of the Rails platform, [...]

4 08 2008
kin

I’m getting an error when responds_to_parent is called.


uncaught exception: Permission denied to get property Window.setTimeout

This happens every time, and sometimes I get this:


uncaught exception: Permission denied to get property Window.setTimeout
getIframeSize()adfetch (line 26)
onload(load )

I believe it’s coming from this chunk of code in responds_to_parent.rb:


# Eval in parent scope and replace document location of this frame
# so back button doesn't replay action on targeted forms
# loc = document.location to be set after parent is updated for IE
# with(window.parent) - pull in variables from parent window
# setTimeout - scope the execution in the windows parent for safari
# window.eval - legal eval for Opera
render :text => "
var loc = document.location;
with(window.parent) { setTimeout(function() { window.eval('#{script}'); loc.replace('about:blank'); }, 1) }
"

Any ideas why this might be happening?

4 08 2008
Apotomo Cookbook » Blog Archive » Apotomo can upload files with AJAX!

[...] this work-around in Rails is quite expensive, an example can be found here. This example uses the responds_to_parent plugin. I’m glad people found a railish solution and released it, but I really dislike the [...]

26 02 2009
iframe remoting pattern & respond_to_parent « dimas priyanto

[...] menghandle form upload di server, kita gunakan plugin responds_to_parent buatan Sean [...]

20 03 2009
Rails Learner

Ha ha ha “Just give me the plugin” – excellent use of anchor text which made me laugh out loud.

25 03 2009
Leena

Hi,

I am getting the error “undefined method `responds_to_parent’ for #” eventhough I have installed the responds_to_parent plugin and restarted the server.

Any help on this will be appreciated.

Thanks,
Leena

30 03 2009
rull_orion

uncaught exception: Permission denied to get property Window.setTimeout

I had this problem and find that it was caused by assets.
it was caused because of “load” or “DomContentLoaded” events that raised on iframe load, and had no permissions to run because it was called from other domain (application.js was on assets host).
So I modified configuration to load prototype and application.js from same host and everything start working.

12 04 2009
jeffrey

Is there a way of using this plugin with jquery?

3 07 2009
Stefan Achatz

I made a small improvement to file responds_to_parent.rb:

7,8c7,9
< def responds_to_parent(&block)
def responds_to_parent
> # don’t force block, user has to make sure he calls this after rendering took place
> yield if block_given?
44a46,51
>
> # try to find out if request is a real ajax one or just a responds_to_parent mock
> def should_respond_to_parent?(request)
> # another way to do it: HTTP_ACCEPT includes ‘text/javascript’ if its an ajax request
> request.params.key?(:format) && request.params[:format] == ‘js’
> end

This makes something like this possible:

module ActionController::Rescue
def render_optional_error_file(status_code)
status = interpret_status(status_code)[0,3]
respond_to do |format|
format.html do
redirect_to :controller => :error_pages, :action => status
end
format.js do
render :update do |page|
page.redirect_to :controller => :error_pages, :action => status
end
respond_to_parent if should_respond_to_parent?(request)
end
format.all do
render :nothing => true, :status => status
end
end
end
end

Now render_optional_error_file is able to respond not only to html-request, but also js and respond_to_parent requests.
And you don’t need to duplicate code for js and respond_to_parent

Leave a comment