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.
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:
[...] and it’s implementation as Ruby on Rails plugin here. Example of using: class TestController < ActionController::Base def upload_action [...]
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.
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
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.
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.
[...] lidar com o formulário no servidor, utilizaremos o plugin responds_to_parent: script/plugin install [...]
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
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.
The “Just give me the plugin.” isnt working, 404 error
Regards
Good catch Rafael. The link now heads on over to Google Code.
[...] http://sean.treadway.info/responds-to-parent/ [...]
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’
[...] 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. [...]
[...] 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. [...]
[...] The responds_to_parent plugin [...]
[...] 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, [...]
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?
[...] 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 [...]
[...] menghandle form upload di server, kita gunakan plugin responds_to_parent buatan Sean [...]
Ha ha ha “Just give me the plugin” – excellent use of anchor text which made me laugh out loud.
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
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.
Is there a way of using this plugin with jquery?
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