Ajax File Upload
Courtenay : April 2nd, 2007
Here’s how to upload without leaving the page. It’s not ajax, but it feels like it. Sorry for the misleading heading :)
First, install the respondsto_parent plugin. Read more about respondsto_parent This plugin lets you fake rjs from an iframe response. Doesn’t make sense? Don’t worry, it will later.
script/plugin install http://sean.treadway.info/svn/plugins/responds_to_parent
Now, in your page, let’s say it’s an article editing form.
<form method="post" action="/articles">
Title: <input type="text" name="article[name]" />
…
Forget about this form.
Add a new form somewhere like the sidebar, looks like this:
<div id="asset_list">
<%= render :partial => ‘asset’, :collection => @assets %>
</div>
<form method="post" action="/assets.js" target="background-uploader" enctype="multipart/form-data" id="background-upload-form">
File: <input type="file" name="asset[uploaded_data]" />
<input type="submit" value="Upload" />
</form>
<iframe width="1" height="1" name="background-uploader" src="about:blank"></iframe>
So, we have a multipart form that points to assets.js with a target, a 1x1 iframe, and a file field. These are all required. Optional, the div with the asset list, but it’s helpful for the user to see all the assets, or perhaps the most recent.
Open up your asset controller and modify the create action.
def create
@asset = Image.create! params[:asset]
respond_to do |wants|
wants.html { redirect_to asset_path(@asset) }
wants.js do
responds_to_parent do
render :update do |page|
page.insert_html :top, ‘asset_list’, render(:partial => ‘articles/asset’, :object => @asset)
page.visual_effect :highlight, “asset_#{@asset.to_param}”
page[‘background-upload-form’].reset
end # render
end # responds_to_parent
end # wants
end # respond_to
rescue ActiveRecord::RecordInvalid
# eep! you’re screwed!
end
What does this do? Well.. first, it creates the asset. Remember how we pointed the form to “assets.js”? This tells rails that we wants.js and rails sets the content-type accordingly. This has the benefit of allowing us to create assets with the same action regardless of iframe or as before.
Unfortunately, since this isn’t ajax, it’s going to dumping its contents as text in the iframe and it won’t work. Enter the responds_to_parent plugin, which does some magic and makes our rjs response activate as expected.
Inside the responds_to_parent block we insert the new asset partial in the top of the list, flash it so the user is aware, and reset the upload form.
And that’s it. Error checking is left to the reader :)
11 Responses to “Ajax File Upload”
Sorry, comments are closed for this article.
April 2nd, 2007 at 07:07 AM
It’s just me, or in Safari it opens a new browser window?
April 2nd, 2007 at 08:09 AM
This is a very clever way to do it! :)
April 2nd, 2007 at 02:46 PM
Safari will ignore the iframe, and open in a new window, if the iframe is invisible, display = none, or the height is 0. Try increasing the height and width of the iframe first. Then try giving a regular width and a height of 1.
April 2nd, 2007 at 04:05 PM
Exactly like I said :)
April 2nd, 2007 at 11:09 PM
Mr. Kahn, you are right, my iframe had a 0 height. Thank you.
April 7th, 2007 at 09:04 AM
I’m not sure If i understand the assets.js correctly - what exactly is it? is it an empty file?
April 9th, 2007 at 10:57 AM
/assets.js is not a file, though it looks like one. This tells Rails (if there is no public/assets.js file :P) to call AssetsController#index with the request type as JS.
April 13th, 2007 at 03:34 PM
This is my Assets controller:
class AssetsController < ApplicationController def create @asset = Asset.new @asset.uploadeddata = params[:uploadeddata] @asset.save
rescue ActiveRecord::RecordInvalid # eep! you”re screwed! end
def index
end end
I’m still getting a routing error for get or post on /assets.js
/assets/create responds as an action but not /assets.js
What did I miss?
April 16th, 2007 at 06:07 AM
Does anyone have a mirror of the responds_to_parent plugin? sean.treadway.info seems to be down.
April 16th, 2007 at 07:04 AM
never mind, found it at koders dot com.
April 18th, 2007 at 11:08 PM
I can’t get this to work. I keep getting a routing error from “/leadsheet.js” which is my controller. How do we specify a particular action, and make it look to rails like a javascript call? I like the theory of the post, being able to dispatch on request type, but I can’t get it to work for me. Please help.