Add links to the step indicator

Created on 21 October 2020, over 4 years ago
Updated 16 February 2023, about 2 years ago

Problem/Motivation

When creating forms with a lot of steps, it would be useful to have links on the step indicator that allows the user to easily go to another part of the form.

Proposed resolution

We did something like this in a form alter hook:

/**
 * @param $form
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 */
function _foo_change_step(&$form, FormStateInterface $form_state) {
  /** @var \Drupal\simple_multistep\MultistepController $multiStep */
  $multiStep = $form_state->get('multistep_controller');
  $multiStep->setFormState($form_state);
  $multiStep->saveInputValues();
  $multiStep->saveStoredValues();

  $trigger = $form_state->getTriggeringElement();

  $currentStep = $multiStep->getCurrentStep();
  $targetStep = $trigger['#parents'][0];

  if ($targetStep > $currentStep) {
    for ($i = $currentStep; $i < $targetStep; $i++) {
      $multiStep->increaseStep();
    }
  }
  elseif ($targetStep < $currentStep) {
    for ($i = $currentStep; $i > $targetStep; $i--) {
      $multiStep->reduceStep();
    }
  }

  $stored_input = $multiStep->getInputValues();
  if (isset($stored_input[$targetStep]) && !empty($stored_input[$targetStep])) {
    $form_state->setUserInput($stored_input[$targetStep]);
  }

  $form_state->set('multistep_controller', $multiStep);
  $form_state->setRebuild();
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * @link https://www.drupal.org/project/simple_multistep/issues/2993675#comment-13069202
 * @see simple_multistep_form_alter()
 */
function foo_form_alter(&$form, FormStateInterface $form_state) {
  /** @var \Drupal\simple_multistep\MultistepController $multiStep */
  if ($multiStep = $form_state->get('multistep_controller')) {
    $currentStep = $multiStep->getCurrentStep();
  }
  else {
    $currentStep = 0;
  }
  $indicator = new StepIndicator($form, $form_state, 0);

  $form['steps_label'] = [
    '#type' => 'container',
    '#weight' => -1,
  ];
  foreach ($indicator->getSteps() as $step_number => $step) {
    $format_settings = $step->format_settings;
    if ($format_settings['show_step_title']) {
      $button = [
        '#type' => 'button',
        '#value' => $step->label,
        '#validate' => ['_foo_change_step'],
      ];
      if ($currentStep == $step_number) {
        $button['#disabled'] = TRUE;
        $button['#attributes']['class'][] = 'active';
      }

      $form['steps_label'][] = $button;
    }
  }
}

(The fact that FormStep::setCurrentStep() is protected forced us to do some weird loops.)

✨ Feature request
Status

Active

Version

1.0

Component

Code

Created by

πŸ‡«πŸ‡·France prudloff Lille

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡©πŸ‡°Denmark ressa Copenhagen

    I agree, this would be an awesome feature. I tried to make the examples work in the current release, and failed with both of them ... so an update and a working example would be fantastic :)

  • πŸ‡ΈπŸ‡°Slovakia coaston

    any update please?

  • πŸ‡§πŸ‡·Brazil joaopauloc.dev

    Hello folks.
    I created an issue to make it easy to extend the MultistepController for another reason. https://www.drupal.org/project/simple_multistep/issues/3501547 ✨ Make MultistepController easy overrided Active
    but I also had to implement the navigation between the steps.
    So, this is how I did.
    Apply my patch in the issue above.
    Extends the MultistepController creating your custom controller.
    Inside this class override the addStepIndicator.
    In my case I created another Step indicator extending from StepIndicator and this is the result.

    /**
     * The ResourceStepIndicator class.
     *
     * @package Drupal\uwas_resource\SimpleMultistep
     */
    class ResourceStepIndicator extends StepIndicator {
    
      /**
       * Render the step indicator.
       *
       * @param array $form
       *   The form array.
       */
      public function render(array &$form): void {
        $form['steps_label'] = $this->createStepsIndicator();
      }
    
      /**
       * Create steps indicator.
       *
       * @return array
       *   Returns the steps label.
       */
      public function createStepsIndicator(): array {
        $data = [
          '#type' => 'container',
          '#weight' => -100,
        ];
        foreach ($this->getSteps() as $step_number => $step) {
          $format_settings = $step->format_settings;
          if ($format_settings['show_step_title']) {
            $button = [
              '#type' => 'button',
              '#value' => $step->label,
              '#validate' => [self::class . '::moveStep'],
              '#step_number' => $step_number,
            ];
            if ($this->currentStep === $step_number) {
              $button['#disabled'] = TRUE;
              $button['#attributes']['class'][] = 'active';
            }
            $data[] = $button;
          }
        }
        return $data;
      }
    
      /**
       * Move step.
       *
       * @param array $form
       *   Form array.
       * @param \Drupal\Core\Form\FormStateInterface $form_state
       *   Form state object.
       */
      public static function moveStep($form, FormStateInterface $form_state): void {
        $multiStep = $form_state->get('multistep_controller');
        $triggering_element = $form_state->getTriggeringElement();
        $step_number = $triggering_element['#step_number'] ?? -1;
    
        $entity_form = $form_state->getFormObject();
        if ($entity_form instanceof EntityFormInterface) {
          $entity_updated = $entity_form->buildEntity($form, $form_state);
          $entity_form->setEntity($entity_updated);
        }
    
        $form_state->set('step', $step_number);
        $multiStep->updateCurrentStep($step_number);
        $form_state->set('multistep_controller', $multiStep);
        $form_state->setRebuild();
      }
    
    }
Production build 0.71.5 2024