Rails Ajax Image Uploading Made Simple with jQuery

Last week, as part of getting Bloggity rolling with the key features of WordPress, I realized that we needed to allow the user to upload images without doing a page reload. Expecting a task as ordinary as this would be well covered by Google, I dutifully set out in search of “rails ajax uploading” and found a bunch of pages that either provided code that simply didn’t work, or claims that it couldn’t be done without a Rails plugin.

The main challenge in getting a AJAX uploading working is that the standard remote_form_for doesn’t understand multipart form submission, so it’s not going to send the file data Rails seeks back with the AJAX request. That’s where the jQuery form plugin comes into play. Here’s the Rails code for it:

$('#uploadForm input').change(function(){
$(this).parent().ajaxSubmit({
beforeSubmit:function(a,f,o){
o.dataType='json';},
complete:function(XMLHttpRequest, textStatus){// XMLHttpRequest.responseText will contain the URL of the uploaded image.// Put it in an image element you create, or do with it what you will.// For example, if you have an image elemtn with id "my_image", then// $('#my_image').attr('src', XMLHttpRequest.responseText);// Will set that image tag to display the uploaded image.},});});

I really like this solution, I currently use it to upload images and then adding hidden fields to another form to link to populate my Product#image_ids on save. I am trying to style the page to make it appear as thought the 2nd form appears within the first so that the flow of the form isn’t broken but its a lot more fiddly that I first thought.

We do something similar for our new item form on Bonanzle. We have a separate image upload form that is declare after the main form body, but you can mouse over an icon in the main form, and it will absolutely position (again, using jQuery) the image form over the main form.

Visit http://www.bonanzle.com, click “Start selling with one click” on home page, and click “Add an Item” to see how it works (and the code). Our solution isn’t that straightforward from a code standpoint, but it’s one pretty functional solution to what is not an easy problem.

Forget my previous post, I found the answer, however (using
Jquery 1.2.6) when doing the render :text => ‘public path of image’
from the controller to send the image back to the browser
the url is wrapped between a

“XMLHttpRequest.responseText = URL to uploaded image. Put it in an img you create or something. ”

Yeah I’m baffled by this too… is this a part of the javascript meant to be completed in situ, as in “XMLHttpRequest.responseText = ‘[path/whatever]’ ” or is “XMLHttpRequest.responseText” used elsewhere?

I am trying upload an image with the same technique. I am trying to save the image to a file rather than to the database (for some initial prototyping). But when I save it to a file, I end up saving a file with contents like “#”. It’s really not saving the image itself

Hi:
I tried this in my rails app with remote_form_for and form_remote_tag and the multipart still does not get to the controller. It looks like the javascript is not getting executed for me. Do I need document.ready for the jquery?

I am trying to use this technique, but the code would just not work for me. The javascript doesn’t seem to get executed as the only parameter that I see in the action is the authenticity token (nothing from the form). Everything works once I put in form_for, but I loose the ajax. I have jrails, jquery, jquery_form, facebox and the form is showing inside a facebox. Any help would be appreciated. Thanks.

Good point. We use the jrails plugin, which automagically translates all of the Rails form helpers (and Rails helpers of any sort) from Prototype to jQuery. If we weren’t using that, then yes, the form would be Prototype. But given the size of both libraries, it doesn’t make much sense to mix jQuery and prototype, so if you’re using jQuery with RAils, chances are you’re using jrails.

I’m pretty sure that even without the jrails plugin this method would still work.

Hey Bolade, hard to figure out what might be going wrong from the limited details. It’d probably make sense to debug in pieces, i.e., is the form being submitted when the input box changes? If not, look at the jquery for how you’re invoking the form submit. If the form gets submitted but is missing the multipart data, then make sure you’ve included jquery-form (which does that particular magic) and jquery. You might read through some of the jquery-form documentation on its particulars to get more data about circumstances under which it might not submit the multipart data.

If I set the data type to ‘script’, do I need to wrap the resulting Javascript in a textarea? If so, how do I do that? Everything I have tried stuffs the textarea tag inside the try/catch block or omits it.

WN :
Forget my previous post, I found the answer, however (using
Jquery 1.2.6) when doing the render :text => €˜public path of image €™
from the controller to send the image back to the browser
the url is wrapped between a

tag.
By removing the

tag in the javascript it works.

Can you please re-post, how you removed the head and body tags around the image url?

One thing I’ve found: this method will not work if you are not using Prototype or jRails (required for remote_form_for). It also will not work with a simple form_for like somebody above suggested. This caused me much grief tonight. Also: if you put any sort of formatting around the input tag (like a paragraph tag) you need to change the js to point to the correct parent – the form – accordingly.

Hi Thanks for your tutorial.
I got it working but have some strange behavior.
The image uploads and it replaces the src of an image on the page.
The first time I upload an image it works.
The second time the image uploads but does not replace the image on the page unless I reload the page. I have been working for days trying to figure out what gives.

Hi
Thank you very much for your tutorial. I am using it and it works very well. Except for one strange thing.
The image upload works the first time. However when I want to upload an image again on the same page it looks like it works but won’t change the image unless I reload the page.

Hi Bill, pretty informative tutorial… I had a thing to confirm….
Aren’t we supposed to add something like in our /app/views/layouts/application.html.erb file….? . Here the file_name_having_js_code would be the file code for javascript as shown above.. I had seen this when basically we make use of jquery.. as per the railscast by Ryan B(http://railscasts.com/episodes/136-jquery).. Just wanted to confirm its addition in the above case also and whether I am actually doing it correctly..

thanks for this great post. i’ve been sweating on this for too long. and its the most simple solution of the lot.
no flash, no iframes, no bulky plugins
instead of a text response i threw in a partial and fed the object to it and ran append on the complete option and it works a treat.
thanks again