3

This is a really strange one, I've created a custom form using the multi step form method. So there are various AJAX calls to handler functions that return certain sections of the form back to containers and replace the HTML.

My form works great has a lot of complexity to it and the speed is fantastic. A recent addition to said form is to be able to upload a PDF, simple I thought! Added my manged_file field and all looked great. However I'd missed that now my page was reloading, AJAX requests were no longer in the Firebug console and instead seemed to be in the POST somehow. I don't fully understand how the AJAX is still working but it is slower that's for sure!

This is a problem I think we could live with a bit of slowness but then I noticed on a full form submission it was breaking a second form outside of this form. Basically it's too big of an issue to live with now.

I've tried a few things, one a standard file field, two a markup field with a raw

<input type="file" /> 

just to see what happened and I experienced the exact same issue as using the form API. This confirms to me that this is not caused by some added javascript from Drupal but a problem with the input itself.

So my question is why does a file field break the AJAX in my form?

Rather awkwardly I can't just paste all my code here as the data is protected and technically not my property. I'm not sure it's needed but I've thrown together a quick bit of code that should demonstrate my issue.

function vv_test_menu(){  
    $items = array();
    $items['vvtest'] = array(
        'page callback' => 'vv_test_page',
        'access arguments' => array('access content'),
        'page arguments' => array(false),
        'description' => t('Test page for code.'),
        'type' => MENU_CALLBACK,
    );
    return $items;
}

function vv_test_page(){
    $content = 'This demonstrates the file field breaking the AJAX.';
    $content .= render(drupal_get_form('vv_test_form'));
    return $content;
}

function vv_test_form($form, &$form_state){
    $form['#tree'] = TRUE;
    $form['#prefix'] = '<div id="container">';
    $form['#suffix'] = '</div>';

    $form['job_number'] = array(
        '#type' => 'textfield',
        '#title' => t('Job number'),
        '#required' => TRUE
    );  

    if($form_state['triggering_element']['#ajax']['callback'] == t('vv_test_call')){
        $form['result'] = array(
            '#markup' => '<div id="container">You entered: '.print_r($form_state['values']['job_number'], true).'</div>'
        );
    }

    // - Comment this field in and out to see the problem
    $form['pdf'] = array(
        '#type' => 'file',
        '#title' => t('A file field')
    );

    $form['sync'] = array(
        '#type' => 'button',
        '#name' => 'sync',
        '#value' => t('Sync'),
        '#ajax' => array(
            'callback' => 'vv_test_call',
            'wrapper' => 'container'
        )
    );

    return $form;
}

function vv_test_call($form, &$form_state){
    $form_state['rebuild'] = FALSE;
    return $form;
}
  • Managed file should never reload on it's own. Can you repeat it with minimal form with only managed file element in it? – Mołot Feb 28 '14 at 15:07
  • Molot I think you've misunderstood it doesn't do anything on its own. Simply having the managed_file field in the form stops AJAX working in the traditional sense. If you've got an environment try the code and you'll see what I mean. – Tim at MastersAllen Mar 3 '14 at 9:18
  • Are you getting any JS errors in the debug console? – mpdonadio Mar 3 '14 at 18:32
  • Nope, first place I looked. – Tim at MastersAllen Mar 11 '14 at 12:12
1

I experienced the exact same: Drupal 7, custom AJAX form, multiple pages, all working fine. After adding a managed file field it broke the AJAX functionality. Or that was what it looked like...

The past couple of days I spent quite some time digging around misc/drupal.js, misc/ajax.js and modules/file/file.js to turn the managed file field into a single customizable "Select & upload file" button with client side file size validation and auto submit functionality.

After reading your question and remark about that the problem even occurred when manually inserting a plain non Form API file field it dawned to me that I read the following in misc/ajax.js > Drupal.ajax.prototype.beforeSend:

For forms with file inputs, the jQuery Form plugin uses the browser's normal form submission mechanism, but captures the response in a hidden IFRAME.

I tested this on the dev site and it appeared that AJAX only "broke" after visiting a form page which contained a plain file field. Though the requests felt less snappy and in Chrome and Firefox loading throbbers appear in the browser tabs and the status bar showed a new page was being requested, it seemed to not harm my forms.

But the jQuery Form plugin for Drupal 7.28 has version number 2.52, see misc/jquery.form.js. The jQuery update module 7.x-2.4 ships with path/to/jquery_update/replace/misc/jquery.form.min.js with version number 2.69, though the latest version is 3.51, see http://malsup.com/jquery/form/#download. This latest version appears to use another technique for file uploads which feels like AJAX (or maybe ís using AJAX), see http://malsup.com/jquery/form/#file-upload. I tried this version on the dev site and so far with success!

Download the latest version to your module or theme and use ie. hook_js_alter() to replace the old script for the new one. I implemented this hook for the site's theme:

/**
 * Implements hook_js_alter().
 */
function YOURTHEME_js_alter(&$js) {
  // Define path to old jQuery Form plugin, alter to your needs.
  $old_path = drupal_get_path('module', 'jquery_update') . '/replace/misc/jquery.form.min.js';

  // The script does not get loaded on every page, so check the $js array.
  if (isset($js[$old_path])) {
    // Define path to newer jQuery Form plugin, alter to your needs.
    $new_path = drupal_get_path('theme', 'YOURTHEME') . '/js/jquery.form.min.js';

    // Copy data from old javascript to new and remove old javascript.
    $js[$new_path] = $js[$old_path];
    unset($js[$old_path]);

    // Set new path as data property and alter version number.
    $js[$new_path]['data'] = $new_path;
    $js[$new_path]['version'] = '3.51.0';
  }
}

Or see how jquery_update_library_alter() did it and apply this method to your module.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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