3

I have a form element that triggers an AJAX callback call when it looses focus. This callback can take a few seconds, which generally is not an issue.

If a user submits the form before the AJAX callback completes, the following error is displayed:

Here is the exact error message displayed to the end user:

An AJAX HTTP request terminated abnormally.Debugging information follows.Path: /system/ajaxStatusText: ResponseText: ReadyState: 4

How can I gracefully terminate an AJAX callback from completing when passing control to a submit handler?

This is the relevant code from the module.

function a_custom_form($form, &$form_state) {
  $form['email'] = array(
    '#prefix' => '<div id="cust-email">',
    '#suffix' => '</div>',
    '#type' => 'textfield',
    '#title' => t('Customer Email Address'),
    '#required' => TRUE,
    '#ajax' => array(
      'callback' => '_user_email_ajax',
      'wrapper' => 'cust-email',
      'effect' => 'fade',
    ),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit Request'),
  );

  return $form;
}

function _user_email_ajax($form, &$form_state) {
  $commands = array();
  // Doing time intensive stuff here...
  drupal_set_message(t('Some Message'), 'error');
  $commands[] = ajax_command_replace('#email-warning-message', '<div id="email-warning-message">'. theme('status_messages') . '</div>');

  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );

}

I have tried the solution given for Prevent "AJAX HTTP request terminated" error on early form submit, but this seems to prevent the submit button from functioning, rather than interrupting the AJAX callback.

3 Answers 3

2

That's not directly possible. There is no API in the browser to manually cancel a sent request. All you can do is to register a callback to know when the HTTP response is received, or at least when the browser changes the state on the xmlHttpRequest object. Drupal's AJAX code already handles that for you. The error message you're seeing is shown because another request was sent which did interrupt the background request, but that still looked as if it was completed (readyState == 4). Drupal considers completely empty responses to be an error and the message is shown.

The most common and user friendly approach I've seen is indeed to disable the submit button(s) while a request is in progress. That in combination with a "spinner" animation or progressbar is a good indication that the user should keep waiting and that they will benefit from it. When the request completes (failed or successful), the buttons are unlocked again.

An input field can become unfocused at any time and for different reasons (maybe the user just wants to copy/paste something into it really quick). Triggering a long running background request whenever that happens could quickly become annoying. As you've already noticed, it interferes with the normal form submit flow as well. Maybe you can work around that and trigger the requests by some other means which more clearly indicate to the user that they need to wait for it to complete? Perhaps giving them the choice to initiate the request when they're sure to be done with manual input into the field and are more inclined to wait for automated processing of their input. Or perhaps trigger the request after a short timer which gets stopped when the field is refocused or the form is submitted.

If not, you could try overriding Drupal's AJAX response interpreting code so it won't show that message if the submit button interrupts the background request, first setting some flag to indicate AJAX errors are to be ignored. Whether this works also depends on how important it is that the AJAX operation on your field completed. If it performs some kind of correction on the [email address?] the user entered, they get it wrong, but the correcting routine didn't get a chance to update the field with valid input, this probably means your serverside validation fails and the user needs to submit it again - which could have been avoided if they had waited for the request to complete.

2
  • If stopping the call gracefully is not possible, would it be possible to simply disregard the callback instead? Or would I still need to override Drupal's default AJAX response to accomplish this?
    – Citricguy
    Commented Mar 27, 2014 at 11:06
  • Yes, the JavaScript callback to process xmlHttpRequest state changes is set by that code, and you'd need to either wrap or replace that callback so it does not alert the user about the empty response. The easiest way would probably be to add a new script file which stores the existing function set on '''Drupal.ajaxError''' in another variable and replace that with your own version, only defaulting to the old function if you don't detect some condition set by a click handler on the submit button (or otherwise detect that the form was submitted normally).
    – TwoD
    Commented Mar 27, 2014 at 11:18
1

If the process is too intensive and you can wait some minutes to execute, a good approach could be to enqueue this process to be executed on the next cron.

Drupal 7 includes queue system in core, references:

In the ajax callback you only need to enqueue this process.

$queue = DrupalQueue::get('mycustomqueue');
$data = array(); //@TODO: include parameters
$queue->createItem($data);

You need to declare your queue:

/**
 * Implements hook_cron_queue_info();
 */
function mymodule_cron_queue_info() {
  $queues['mycustomqueue'] = array(
    'worker callback' => ''mycustomqueue_queue_callback',
    'time' => 60,
  );
  return $queues;
}

Callback function (drupal get all queued items and calls one by one):

function mycustomqueue_queue_callback($queue_item) {
  $data = $queue_item['data'];
  // DO stuff!
}
0

You can do a trick: hide the submit button on focus with jquery until the ajax response is executed ( and the show it again with an ajax command).

1
  • The ajax in this case is not critical to the form itself and I would prefer it not block the UI if possible.
    – Citricguy
    Commented Mar 27, 2014 at 11:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.