The drop is always movingYou know that saying about standing on the shoulders of giants? Drupal is standing on a huge pile of midgetsAll content management systems suck, Drupal just happens to suck less.Popular open source software is more secure than unpopular open source software, because insecure software becomes unpopular fast. [That doesn't happen for proprietary software.]Drupal makes sandwiches happen.There is a module for that

Drupal 7 multistep node forms are easy

Submitted by nk on Mon, 2010-03-08 01:21

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.

Submitted by Anonymous on Fri, 2011-07-22 05:56.

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 :)