Drupal → Показать второй шаг многошаговой формы в модальном окне

05.02.2024

По мотивам предыдущего поста про многошаговые формы — пример двухшаговой формы, в которой второй шаг показывается в модальном окне:

class ExampleTwoStepForm extends FormBase {

  /**
   * {@inheritDoc}
   */
  public function getFormId(): string {
    return 'example_two_step_form';
  }

  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $step = (int)$form_state->get('step');
    if (!$step) {
      $step = 1;
      $form_state->set('step', $step);
      $form_state->set('steps_values', []);
    }

    return $this->{'buildStep' . $step}($form, $form_state);
  }

  /**
   * Build step 1.
   */
  public function buildStep1(array $form, FormStateInterface $form_state): array {
    $form['first_name'] = [
      '#type' => 'textfield',
      '#title' => 'First name',
    ];

    $form['next'] = [
      '#type' => 'submit',
      '#value' => 'Next',
      '#name' => 'next',
      '#submit' => ['::nextButtonSubmit'],
      '#ajax' => ['callback' => '::nextButtonAjax'],
    ];

    return $form;
  }

  /**
   * "Next" button submit callback.
   */
  public function nextButtonSubmit(array $form, FormStateInterface $form_state): void {
    $form_state->set('step', 2);

    // Store current values to all-steps values
    $steps_values = $form_state->get('steps_values') ?? [];
    $steps_values = NestedArray::mergeDeep($steps_values, $form_state->cleanValues()->getValues());
    $form_state->set('steps_values', $steps_values);

    // Copy all-steps values to current values
    $form_state->setValues($steps_values);

    // Disable form reload (redirect)
    $form_state->setRebuild();
  }

  /**
   * "Next" button ajax callback.
   */
  public function nextButtonAjax(array $form, FormStateInterface $form_state): AjaxResponse {
    $response = new AjaxResponse();
    
    $response->addCommand(new UpdateBuildIdCommand($form['#build_id'], $form['#build_id_old']));
    
    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
    $response->addCommand(new OpenModalDialogCommand('Step 2', $form));
    
    return $response;
  }

  /**
   * Build step 2.
   */
  public function buildStep2(array $form, FormStateInterface $form_state): array {
    $form['last_name'] = [
      '#type' => 'textfield',
      '#title' => 'Last name',
    ];

    $form['finish'] = [
      '#type' => 'submit',
      '#value' => 'Submit',
      '#name' => 'finish',
    ];

    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $steps_values = $form_state->get('steps_values') ?? [];
    $values = NestedArray::mergeDeep($steps_values, $form_state->cleanValues()->getValues());
    dsm($values);
  }

}

Как всегда не обошлось без подводных камней. Если пользователь нажал на кнопку "Next", потом закрыл модальное окно и снова нажал кнопку "Next", то вызовется submitForm(), а не nextButtonSubmit(), как ожидается. Так происходит потому, что перед открытием модалки друпал обновит form_build_id основной формы и при последующих отправках будет думать, что пользователь сабмитит второй шаг. Именно поэтому в nextButtonAjax() возвращаем старый form_build_id с помощью new UpdateBuildIdCommand($form['#build_id'], $form['#build_id_old'])

Демо модуль.

Написанное актуально для
Drupal 8+
Похожие записи

Комментарии

Добавить комментарий