How to Submit a Drupal Webform Via AJAX

Drupal's Form API (FAPI) is, in my opinion, one of Drupal's most powerful features and compared to many other API's I've worked with pretty developer friendly. However, as with every API there are gotchas that one must be aware of. One such gotcha that I came across recently was trying to use hook_form_alter to submit a webform via AJAX. Below are the steps I took, a problem I encountered, and how I resolved it. Presumably if you are reading this, you already know how to create a webform using Drupal's webform module, so I will not cover that here. If not, there are plenty of tutorials out there to help you understand how to use this incredibly powerful module.

First let's start off with a simple webform like the one you see below.

Webform Screenshot

Here we have a typical contact form with 4 required fields. Webform allows you to configure the form to show a confirmation message or redirect to a valid path upon successful submission; however, both options require a page reload. Wouldn't it be nice if we didn't need to reload the page and could simply submit the form via AJAX? Fortunately FAPI allows us to easily accomplish this task by altering the form's array.

To do this we'll use Drupal's hook_form_alter function and add the #ajax key/value pair. Here the #ajax key is an associative array with 3 items, callback, wrapper, and effect. Callback is the function that will be used to process the form. Wrapper is the id of the html element that will be replaced after the form is processed. Effect is the type of transition (fade or slide) that will be used to move from the form to the confirmation or error state.


/** * Implements hook_form_alter(). */ function custom_form_alter(&$form, &$form_state, $form_id) { switch($form_id) { case 'webform_client_form_1': $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), '#ajax' => array( 'callback' => 'custom_webform_client_form_1_js_submit', 'wrapper' => 'webform-client-form-' . $form['#node']->nid, 'effect' => 'fade', ), ); break; } }

The next bit of code is the callback function used to process the form. In this function we check for a submission id which is only created after the form validates and the submission is saved. If a we get a submission id we'll return the confirmation message which will replace the contents of the wrapper we defined in the #ajax key. If we don't get a submission id, there was an error in submitting the form, so we'll return the form for the user to try again.


function custom_webform_client_form_1_js_submit($form, $form_state) { $sid = $form_state['values']['details']['sid']; if ($sid) { $node = node_load($form_state['values']['details']['nid']); $confirmation = array( '#type' => 'markup', '#markup' => check_markup($node->webform['confirmation'], $node->webform['confirmation_format'], '', TRUE), ); return $confirmation; } else { return $form; } }

If you try this code, you will notice that your form is still submitting with a page reload. I reviewed the code again and again and it seemed correct. I've done this successfully before with forms I created programatically, so I figured that there must be something going on with webform forms that was different.

Finally, after some digging and printing out the form array, I found that Webform 4.x implements a function called webform_pre_render_remove_id that replaces ids with classes wherever possible in order to avoid duplicate ids. See https://www.drupal.org/node/2059215 for more info.

Unfortunately, this function removes the id from the submit button needed to attach the necessary onclick event handler for AJAX submission to work. To fix this simply add one more line to the form alter that adds an id to the submit button.


/** * Implements hook_form_alter(). */ function custom_form_alter(&$form, &$form_state, $form_id) { switch($form_id) { case 'webform_client_form_1': $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), '#ajax' => array( 'callback' => 'custom_webform_client_form_1_js_submit', 'wrapper' => 'webform-client-form-' . $form['#node']->nid, 'effect' => 'fade', ), '#id' => 'edit-submit--' . $form['#node']->nid, ); break; } }

Tags: Drupal 7, AJAX, Webforms