Implementing this from scratch could be quite some work. Fortunately, there’s a jQuery plugin to save us some time and headaches.
About the plugin
It adds 2 options to the
1 2 3 4
iframeexpects a boolean, set it to true when you wan’t to upload files.
filesexpects a jQuery object of one or many file fields.
What it will do is exactly the trick mentioned above. Will set up a new iframe, a new form with the
target attribute set to the
name of the iframe, and then just listen to the
load event on the iframe.
There are a couple of extra things good to know when using this plugin:
- It adds a
X-Requested-With=IFramevalue to the form. That’s useful in case you need to do any special processing in the backend.
- If you’re sending along other data with the request, you must need to send it in JSON form, it does not accept an already serialized data string. That’s because it creates a new hidden input field inside the form for each value it submits.
A bit about Backbone
To understand what we’re going to do, we first need to know a few things about how Backbone works.
Under the hood,
sync to communicate with your server, passing along the
options object (with a few modifications). We’re going to take advantage of that.
To illustrate this quick example, imagine 2 simple parts of the app.
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
When the form is submitted we’re are going to serialize all form’s data, and save it to the model. And we’re gonna add a few extra options (
data), that will get passed to the
sync method, and finally to the
As we saw before,
iframe: true means that the request is going to use the iframe transport. The
files option, is set to the form files fields. And finally, we need to specify
data values again. That should be the same data we want to persist on the model, but as explained above, the iframe transport methods needs it to be a JSON object.
That’s already working! Quite easy, except for a few quirks we need to be aware of.
Always resolves as success
As the plugin listens to the
load event of an iframe, it can’t know the HTTP status code of the server response. That means, it can’t tell wether the response was a 2xx, 5xx, or any other thing. All calls will be triggering a
success() callback, never a
You should determine if it was OK or not by looking at the response payload. It’s wise to return a JSON with some message if the response wasn’t successful.
Unknown Response Data Types
Again, because it just listens to a
load event, it does not have access to the HTTP headers of the server response. This makes it harder to use the automatic content-type detection provided by jQuery.
Fortunately, in this case there’s a simpler workaround: Wrap the data in a
<textarea> element with a
data-type attribute specifying the content type.
1 2 3
This is specially important when using IE. It will trigger a download alert if it can’t detect the content type.
Using it with Rails
I’ve used this solution with a Ruby on Rails app, and found a few more things worth noting.
If you’re using
protect_from_forgery on your controllers, there’s a good chance that you need an extra tweak to make this work. That is, passing the authenticity_token as a parameter too.
Just modify the
uploadFile function a bit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
It’s kinda ugly, but still better than to remove the CSRF-protection from your controller.
Middleware with textarea wrapping
And finally, to make it easy to wrap the response in a textarea when needed, there’s a nice middleware. It was created by the author of the jquery-fileupload-rails, which uses the iframe transport under the hood.
Go take a look at it. It’s quite handy!
Uploading files with Backbone via ajax while maintaining compatibility for older browsers can be a thing. Hope this helps somebody!
savecan receive 2 or 3 arguments: (
options), or (