0

I am building a multistep form using ctools, basically following this approach. Now I want the following behaviour:

  • user is at step 1
  • enters data for step 1
  • clicks continue and gets to step 2
  • enters data for step 2
  • clicks back and gets to step 1
  • sees the data he entered previously (works already)
  • clicks continue and gets to step 2
  • sees the data he entered previously (works not yet)

To show the previously entered data after clicking continue, I simply set $form[$name]['#default_value'] with values from $form_state['values'] (with a detour via ctools object cache). So this works already.

The problem is with clicking back: After clicking back, $form_state['values'] does not contain any entered values. So I don't have the values to populate $form[$name]['#default_value'] and this is my current problem.

So I wonder: How can I send entered values with the back button submit?

5 Answers 5

1

To get ctools wizard to save values when the user presses the back button, you would have to remove it's '#limit_validation_errors' property that is set by default in ctools_wizard_wrapper(). There is no convenient switch or form_info property to do this so you have to use a form alter hook similar to this:

function test_form_alter(&$form, &$form_state, $form_id) {
  if (isset($form_state['wrapper_callback']) && $form_state['wrapper_callback'] == 'ctools_wizard_wrapper' && $form_state['form_info']['id'] == 'test_wizard') {
    if (isset($form['buttons']['previous'])) {
      unset($form['buttons']['previous']['#limit_validation_errors']);
    }
  }
}
0

As far as I am aware there is not a built in way to achieve this within cTools with the wizard code you are probably playing with (the sub modules that came with cTools inside its plugins folder).

You'll have to use session storage on the server side or localstorage on the client side (javascript) to have the values saved in the manner you describe.

For instant results, install Multi-step forms module and browse to http://your-install.com/mforms/example/session to see a working example of what you describe. The corresponding code is inside of Multi-step forms, which is made for developers to utilize.

1
  • Thanks for your answer and the hint to mforms. I've tried it out but it's not really convincing me, as there's no "native" back button (it seems you have to add it to the forms by yourself). Additionally, its currently pretty low popularity scares me off a bit. So I decided to keep using ctools and postpone the desired feature - and in case I'll need it once, I think using the $_POST var could be a working hack. Commented Dec 20, 2012 at 2:44
0

I am able to solve the problem. Actually in my case $form_state['rebuild'] = TRUE; is causing the problem. This actually refresh the form and $form_state['values'] is set to null. So I have created an array which will hold $form_state['values'] before it get refreshed and store it in each step storage form. A bit of code to explain you better.

function form_user_registration_previous_submit($form, &$form_state) {
    $current_step = &$form_state['step'];    
    $formvalue_array=$form_state['values'];
    $form_state['step_information'][$current_step]['stored_values'] = $formvalue_array;
    if ($current_step > 1) {
      $current_step--;
      $form_state['values'] = $form_state['step_information'][$current_step]['stored_values'];
    }
    $form_state['rebuild'] = TRUE;   
  }

function form_user_registration_next_submit($form, &$form_state) {
    $current_step = &$form_state['step'];
    $formvalue_array=$form_state['values'];    
    $form_state['step_information'][$current_step]['stored_values'] = $formvalue_array;
    if ($current_step < count($form_state['step_information'])) {
      $current_step++;
      if (!empty($form_state['step_information'][$current_step]['stored_values'])) {
        $form_state['values'] = $form_state['step_information'][$current_step]['stored_values'];
      }
      else {
        $form_state['values'] = array();
      }
      $form_state['rebuild'] = TRUE;  // Force rebuild with next step.      
      return;
    }
  }
0

I know this question is a bit old, but might help someone in the same situation as I was.

If you end up creating custom submit handlers and having to take care of saving the step values manually then you are not fully taking advantage of what ctools has to offer. The documentation on using and creating multi step forms is quite poor, but following this blog post helped me understand better all the different callbacks that the ctools wizard offers: http://www.grasmash.com/article/building-multistep-registration-form-drupal-7-using-ctools

The only thing missing from the 'next callback' in that tutorial is 'merging' the existing values from the cache with the new ones from your current step (see below).

When you define your '$form_info' and specify all the ctools steps and options, you have the ability to set callback for 'next', 'finish', 'return', and 'cancel' actions. The way I ended up saving my step data when going back and forward is by setting the 'next callback' and then saving the $form_state['values'] in the ctools object cache.

'next callback' => 'my_module_subtask_next',

Then in the actual callback function you basically merge your current cached object with the new $form_state['values']:

function my_module_subtask_next(&$form_state) {
  $values = (array) my_module_get_page_cache('my_module');
  my_module_set_page_cache('my_module', array_merge($values, $form_state['values']));
}

Then when setting up your $form fields you can retrieve this ctools cache object and set your #default_value:

// ctools automatically adds the cache object in your $form_state
$form_values = $form_state['my_module_object'];
$form['example'] = array(
  '#type' => 'texfield',
  '#title' => t('Example field'),
  '#default_value' => (isset($form_values['example'])) ? $form_values['example'] : NULL,,
);
0

Yes this is possible, but you need a workaround due to Back callback : overloading and call

/**
 * Callback executed when the 'back' or 'next' button is clicked.
 */
function MODULE_next(&$form_state) {
  if ($form_state['clicked_button']['#value'] == t('Back')) {
    // Do whatever, but use $form_state['input'] instead of $form_state['values']
  }
  else {
    // Use $form_state['values']
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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