File uploads are finally deployed! You can now specify in MyOwnDB that a detail of your entity is of the type "file". This will let you attach a file to an entry, and subsequently replace or delete it. This required some changes in the application, more specifically in the way the forms are submitted back to the server.
For a file upload to work fine, you have to send it with the enctype attribute set to "multipart/form-data". But that's not all! You cannot just serialize the form in javascript like you can do with other input types because you cannot access the file from Javascript. The reason for this is security.
Dojo to the rescue
Luckily, the
Dojo javascript toolkit has a workaround that works amazingly well, and is really easy to put in place. Dojo is facilitating all XMLHttprequest thanks to its dojo.io.bind method, which I discovered thanks to an article correctly title "
Introduction to dojo.io.bind". This article shows that dojo.io.bind can be used to send simple GET or POST request, but also to submit forms. At first, I thought it didn't support file uploads (as stated in the document), but it does amazingly well. And, best of all, it is cross browser.
Installing Dojo in Rails
I
downloaded the AJAX edition and uncompressed it in the public/javascript/dojo directory of the rails application. I then put the following code in my layout:
This code first sets the base url from which Dojo will be served, and then includes the main dojo.js file. Once that's done, you can normally include new functionalities thanks to dojo's require mechanism, like shown for dojo.io.*. I had problems requiring the IframeIO code though, and ended including the javascript file manually.
But, I hear you say, Javascript doesn't have access to the file. And you're right. The trick is that dojo.io.bind can work with different transport, in a way totally transparent for the developer. So when dojo.io.bind sees you submit a form with file upload, it doesn't use the
XMLHttp transport, but its
IFrame I/O transport.
Converting remote form to dojo.io.bind
dojo.io.bind takes a hash as parameter. When the request is completed, it will call the function it got under the "load" key in the hash (and will similarly call the function it gets under the "error" key in case of error) . This means that it is extremely easy to convert Rails' remote_forms to dojo. Here is an example of replacing a form in a erb template:
<%= form_remote_tag(:multipart => true ,:url => { :controller => "my_controller", :action => "my_action" }, :complete => "my_javascript_code();" %>
by a dojo.io.bind equivalent: