Permalink
Browse files

Refactored and added check to abort ajax form submission if non-blank

file uploads are present. Added would-be test since this cannot be autotested.
Added triggered callbacks for when ajax form submission is aborted,
either due to missing required inputs or non-blank file upload fields, to make it easy for custom handler binding.
ajax:aborted:file trigger allows overriding of ajax file upload
functionality, but still submits the form normal-style by default.
  • Loading branch information...
1 parent 7f2acc1 commit ca575e184e93b3efe1a858cf598f8a37f0a760cc @JangoSteve JangoSteve committed with Neeraj Singh Feb 10, 2011
Showing with 67 additions and 7 deletions.
  1. +44 −7 src/rails.js
  2. +23 −0 test/public/test/call-remote-callbacks.js
View
@@ -3,6 +3,27 @@
*
* Requires jQuery 1.4.3 or later.
* https://github.com/rails/jquery-ujs
+
+ * Uploading file using rails.js
+ *
+ * By default, browsers do not allow files to be uploaded via AJAX. As a result, when this rails.js adapter submits remote forms,
+ * any file input fields are excluded from the request parameters sent to the server. You may cancel the whole form submission by
+ * binding a handler function that returns false to the `ajax:aborted:file` hook.
+ *
+ * Ex:
+ * $('form').live('ajax:aborted:file', function(){
+ * alert("File detected. Form submission canceled.");
+ * return false;
+ * });
+ *
+ * The `ajax:aborted:file` event is fired when a form is submitted and both conditions are met:
+ * a) file-type input field is detected, and
+ * b) the value of the input:file field is not blank.
+ *
+ * Third party tools can use this hook to detect when an AJAX file upload is attempted, and then use techniques like the iframe method to upload the file instead.
+ *
+ * Similarly, rails.js aborts AJAX form submissions if any non-blank input[required] fields are detected, providing the `ajax:aborted:required` hook.
+ * Unlike file uploads, however, blank required input fields cancel the whole form submission by default.
*/
(function($) {
@@ -104,12 +125,22 @@
return !message || (fire(element, 'confirm') && confirm(message));
}
- function requiredValuesMissing(form) {
- var missing = false;
- form.find('input[name][required]').each(function() {
- if (!$(this).val()) missing = true;
+ function blankInputs(form, specifiedSelector) {
+ var blankExists = false,
+ selector = specifiedSelector || 'input';
+ form.find(selector).each(function() {
+ if (!$(this).val()) blankExists = true;
});
- return missing;
+ return blankExists;
+ }
+
+ function nonBlankInputs(form, specifiedSelector) {
+ var nonBlankExists = false,
+ selector = specifiedSelector || 'input';
+ form.find(selector).each(function() {
+ if ($(this).val()) nonBlankExists = true;
+ });
+ return nonBlankExists;
}
$('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) {
@@ -129,8 +160,14 @@
var form = $(this), remote = form.data('remote') != undefined;
if (!allowAction(form)) return false;
- // skip other logic when required values are missing
- if (requiredValuesMissing(form)) return !remote;
+ // skip other logic when required values are missing or file upload is present
+ if (blankInputs(form, 'input[name][required]')) {
+ form.trigger('ajax:aborted:required');
+ return !remote;
+ }
+ if (nonBlankInputs(form, 'input:file')) {
+ return fire(form, 'ajax:aborted:file');
+ }
if (remote) {
handleRemote(form);
@@ -81,6 +81,29 @@ asyncTest('blank required form input field should abort request', 1, function()
}, 13);
});
+function skipIt() {
+ // This test cannot work due to the security feature in browsers which makes the value
+ // attribute of file input fields readonly, so it cannot be set with default value.
+ // This is what the test would look like though if browsers let us automate this test.
+ asyncTest('non-blank file form input field should abort request', 1, function() {
+ var form = $('form[data-remote]')
+ .append($('<input type="file" name="attachment" value="default.png">'))
+ .bind('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend should not run');
+ })
+ .bind('iframe:loading', function() {
+ ok(true, 'form should not get submitted');
+ })
+ .trigger('submit');
+
+ setTimeout(function() {
+ form.find('input[type="file"]').val('');
+ form.unbind('ajax:beforeSend');
+ submit();
+ }, 13);
+ });
+}
+
asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', 1, function() {
$('form[data-remote]').live('ajax:beforeSend', function() {
ok(true, 'ajax:beforeSend observed with event delegation');

8 comments on commit ca575e1

Contributor

RStankov replied Apr 12, 2011

I think, it will be good if I could do something like:

 $(document).bind('ajax:aborted:required', function(){
    return false;
 });

And stop the aborting.

Agree, unexpected behaviour

Member

JangoSteve replied Apr 12, 2011

@RStankov and @rubylibre, see @mislav's explanation here for why it cancels the ajax submission by default.

Basically, Opera does not submit (i.e. fire the submit event for) forms with blank required input fields. If we allowed the form to be submitted, either by default or by returning false on the ajax:aborted:required event, what we'd actually end up with is inconsistent behavior across browsers, in that it would work in everything but Opera, and Opera would still continue to not submit the form.

Of course, we're probably already getting some inconsistent behavior, in that the ajax:aborted:file event probably doesn't get triggered in Opera. This event was really just meant to be a broadcast of when jquery-ujs is doing something (when the browser makes it necessary to step in), rather than a way to control behavior.

Where's Opera in the 80:20 rule? :)

Contributor

RStankov replied Apr 13, 2011

too bad, this reminds me why I really dislike Opera :(

Member

neerajdotname replied Apr 13, 2011

Opera has 2% market share . http://blogs.sitepoint.com/ie6-usage-below-5-percent-browser-trends/

I will say we don't need to worry about opera. They have a lot more market share in mobile world but that's a different world.

Member

JangoSteve replied Apr 13, 2011

Neeraj has spoken, it shall be done :-) I'll submit a pull request with this ability along with a note that it will not be fired in Opera.

Member

JangoSteve replied Apr 14, 2011

Ok, I've submitted a pull request the ability to cancel the ajax:aborted:required event (to submit the form anyway), along with tests.

Please sign in to comment.