Creating a custom submit button with webform API
I've been building some custom components with the webform API. Each component contains a set of related form fields, and one of them has a number of different fieldsets, each with its own submit button. We'll theme this component to make it appear as if there are multiple forms on the page.
It's easy to add extra submit buttons through the webform API, but harder to make them work. When I hit one of my component submit buttons, the form would reload the same page instead of submitting the form, or going to the next page.
It turns out that webform has some strict requirements for the structure of its submit and next page buttons which allows webform to handle features like multi-page forms, saving as draft, and configurable submit button text.
One of webform's #submit handlers, webform_client_form_pages(), checks the clicked button to figure out if it needs to save the form or display the next or previous page.
Submitting a webform
Here's how webform_client_form_pages() determines if the form needs to be submitted:
// Check for a multi-page form that is not yet complete.
$submit_op = !empty($form['actions']['submit']['#value']) ? $form['actions']['submit']['#value'] : t('Submit');
$draft_op = !empty($form['actions']['draft']['#value']) ? $form['actions']['draft']['#value'] : t('Save Draft');
if (!in_array($form_state['values']['op'], array($submit_op, $draft_op))) {
In this if statement, we're checking the clicked button to see if it's one of "Submit" or "Save Draft". If the webform has a custom submit button text, we check for that instead.
If the clicked button doesn't match, we won't submit/save the form, and instead assume we're in a multi-page form that needs to be continued.
Since my component buttons weren't named "Submit", the form wasn't saving.
But I can't just change the value of my component button to "Submit", because if the webform has a custom submit button text we'll still fail. And if we're on the first page of a multi-page form, we won't do the right thing.
To get webform to respect my component submit button I added a validation function to the webform which sets $form['values']['op'] to match webform's expectations:
if (isset($form['actions']['submit'])) {
$form_state['values']['op'] = $form['actions']['submit']['#value'];
}
Multi-page web form
If my component is on a non-final page of a multi-page webform, there is no submit button, instead webform provides a next page button.
Here's how webform_client_form_pages() determines that the next page button was clicked:
if (end($form_state['clicked_button']['#parents']) == 'next') {
$direction = 1;
}
else {
$direction = 0;
}
In order to mimic this functionality in my component submit button, I have to set $form_state['clicked_button']['#parents'] in the validation function. This has a bad smell, but I haven't found a better option yet:
elseif (isset($form['actions']['next'])) {
['#value'];
$form_state['clicked_button']['#parents'] = array(
'actions',
'next',
);
}
Validation function
Here's the full validation function:
function mymodule_validate_submit($form, &$form_state) {
if (isset($form['actions']['submit'])) {
$continue_op = $form['actions']['submit']['#value'];
}
elseif (isset($form['actions']['next'])) {
$continue_op = $form['actions']['next']['#value'];
$form_state['clicked_button']['#parents'] = array(
'actions',
'next',
);
}
if (isset($continue_op)) {
$form_state['values']['op'] = $continue_op;
}
}