The bane of multistep node forms are validation errors. But Drupal 7 has a cure. Not nice but a cure.
Slap #access = FALSE
on form elements you dont want to see in the current step and then set up the Next button with #limit_validation_errors
set to the elements you are about to see. The Prev button should get an empty #limit_validation_errors
. Should field validation errors bother you, you can nuke those in hook_field_attach_validate
.
<?php
define('MYMODULE_STORY_NODE_FORM_LAST_STEP', 2);
function mymodule_form_story_node_form_alter(&$form, $form_state) {
$step = isset($form_state['storage']['step']) ? $form_state['storage']['step'] : 1;
if ($step > 1) {
$form['actions']['previous'] = array(
'#type' => 'submit',
'#value' => t('Previous'),
'#submit' => array('mymodule_node_form_previous', 'node_form_submit_build_node'),
// -1 so that it appears before the "Next" button added later.
'#weight' => -1,
'#limit_validation_errors' => array(),
);
}
if ($step < MYMODULE_STORY_NODE_FORM_LAST_STEP) {
$form['actions']['next'] = array(
'#type' => 'submit',
'#value' => t('Next'),
'#submit' => array('mymodule_node_form_next', 'node_form_submit_build_node'),
);
}
$steps = mymodule_node_form_by_step();
foreach ($steps as $key => $step_current) {
if ($step_current == $step) {
if (isset($form['actions']['next'])) {
$form['actions']['next']['#limit_validation_errors'][] = array($key);
}
}
else {
$form[$key]['#access'] = FALSE;
}
}
}
function mymodule_node_form_by_step() {
static $steps = array(
// -1 means never show.
'author' => -1,
'revision_information' => -1,
'title' => 1,
'body' => 2,
);
return $steps;
}
function mymodule_node_form_next($form, &$form_state) {
if (!isset($form_state['storage']['step'])) {
$form_state['storage']['step'] = 1;
}
if ($form_state['storage']['step'] == MYMODULE_STORY_NODE_FORM_LAST_STEP) {
$form_state['rebuild'] = FALSE;
}
else {
$form_state['rebuild'] = TRUE;
$form_state['storage']['step']++;
}
}
function mymodule_node_form_previous($form, &$form_state) {
$form_state['rebuild'] = TRUE;
$form_state['storage']['step']--;
unset($form_state['node_preview']);
}
function mymodule_field_attach_validate($obj_type, $object, &$errors) {
if (arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit')) {
// foo and bar fields are handled elsewhere so nuke their errors.
unset($errors['foo'], $errors['bar']);
}
}
?>
Commenting on this Story is closed.
Great example of using the default node form structure to do multistep!
I'm trying to work out if it can be AJAX-ed so that only the form is loaded with each step rather than the entire page? Might this be possible or do I need to build such a form outside of the core node form?
Rick :)