Mikhail Ismajlov 09/30/2019

In this article we will continue to understand Form API in Drupal 8 and make a multistep form. We have already created the usual configuration form for the module, the multistep form is created in a similar way using $form_state to store data between the form steps.

Examples of code can be found at github:

https://github.com/levmyshkin/drupalbook8

For a multistep form you need to add a form class:

/modules/custom/drupalbook/src/Form/MultiStepForm.php

/**
 * @file
 * Contains Drupal\drupalbook\Form\MultiStepForm.
 */
 
namespace Drupal\drupalbook\Form;
 
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
 
class MultiStepForm extends FormBase
{
 
  protected $step = 1;
 
  /**
   * {@block}
   */
  protected function getEditableConfigNames()
  {
  }
 
  /**
   * {@block}
   */
  public function getFormID()
  {
    return 'multi_step_form';
  }
 
  /**
   * {@block}
   */
  public function buildForm(array $form, FormStateInterface $form_state)
  {
    //$form = parent::buildForm($form, $form_state);
 
    // Add a wrapper div that will be used by the Form API to update the form using AJAX
    $form['#prefix'] = ';
    $form['#suffix'] = '
';
    if ($this->step == 1) {
      $form['message-step'] = [
        '#markup' => '. $this->t('Step 1 of 2') . '
',
      ];
      $form['message-title'] = [
        '#markup' => '
' . $this->t('Who are you?') . '
',
      ];
      $form['first_name'] = [
        '#type' => 'textfield',
        '#title' => $this->t('First name'),
        '#placeholder' => $this->t('First name'),
        '#required' => TRUE,
      ];
      $form['last_name'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Last name'),
        '#placeholder' => $this->t('Last name'),
        '#required' => TRUE,
      ];
 
    }
 
    if ($this->step == 2) {
      $form['message-step'] = [
        '#markup' => '. $this->t('Step 2 of 2') . '
',
      ];
      $form['message-title'] = [
        '#markup' => '
' . $this->t('Please enter your contact details below:') . '
',
      ];
      $form['phone'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Phone'),
        '#placeholder' => $this->t('Phone'),
        '#required' => TRUE,
      ];
      $form['email'] = [
        '#type' => 'email',
        '#title' => $this->t('Email address'),
        '#placeholder' => $this->t('Email address'),
        '#attributes' => array('class' => array('mail-first-step')),
        '#required' => TRUE,
      ];
      $form['subscribe'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Subscribe to newsletter'),
      ];
      $form['agree'] = [
        '#markup' => '. $this->t(' By signing up you agree to the ,
            array('@terms' => '/terms-and-conditions', '@policy' => '/privacy-policy')) . '
',
      ];
    }
 
    if ($this->step == 3) {
      $form['message-step'] = [
        '#markup' => '. $this->t('- Complete -') . '
',
      ];
      $form['message-title'] = [
        '#markup' => '
' . $this->t('Thank you') . '
',
      ];
 
    }
 
    if ($this->step == 1) {
      $form['buttons']['forward'] = array(
        '#type' => 'submit',
        '#value' => t('Next'),
        '#prefix' => ',
        '#suffix' => '
',
        '#ajax' => array(
          // We pass in the wrapper we created at the start of the form
          'wrapper' => 'ajax_form_multistep_form',
          // We pass a callback function we will use later to render the form for the user
          'callback' => '::ajax_form_multistep_form_ajax_callback',
          'event' => 'click',
        ),
      );
    }
    if ($this->step == 2) {
      $form['buttons']['forward'] = array(
        '#type' => 'submit',
        '#value' => t('Submit'),
        '#ajax' => array(
          // We pass in the wrapper we created at the start of the form
          'wrapper' => 'ajax_form_multistep_form',
          // We pass a callback function we will use later to render the form for the user
          'callback' => '::ajax_form_multistep_form_ajax_callback',
          'event' => 'click',
        ),
      );
    }
 
    $form['#attached']['library'][] = 'drupalbook/multistep_form';
    return $form;
  }
 
  /**
   * {@block}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    return parent::validateForm($form, $form_state);
  }
 
  /**
   * {@block}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    if ($this->step == 2) {
      $values = $form_state->getValues();
      $email = $values['email'];
      // Save data or send email here.
    }
 
    $this->step++;
    $form_state->setRebuild();
  }
 
  public function ajax_form_multistep_form_ajax_callback(array &$form, FormStateInterface $form_state) {
    return $form;
  }
 
}?>

Next we'll figure out what each of these lines does. In the meantime, let's create a route for our form:
/modules/custom/drupalbook/drupalbook.routing.yml:

drupalbook.multistep_form:
  path: '/multistep-form'
  defaults:
    _form: '\Drupal\drupalbook\Form\MultiStepForm'
    _title: 'Subscribe to newsletter'
  requirements:
    _permission: 'access content'

This will create a form at /multistep-form:

search

In order for this form to open in a modal pop-up window, you need to add a block or just text with a specific use-ajax class and data-dialog-type="modal" attribute, with a link to the form page. You will also need to enable the following libraries: core/drupal.dialog.ajax, core/jquery.form. If you do this through the block, you will get the following code for the form open button in the popup:modules/custom/drupalbook/src/Plugin/Block/SubscribeFormButton.php

namespace Drupal\drupalbook\Plugin\Block;
 
use Drupal\Core\Block\BlockBase;
 
/**
 * Provides a button subscribe to newsletter.
 *
 * @block(
 *   id = "drupalbook_subsribe_form_button",
 *   admin_label = @block("Subscribe to Newsletter"),
 * )
 */
class SubscribeFormButton extends BlockBase {
  /**
   * {@block}
   */
  public function build() {
    $text = 'Subscribe';
 
    return [
      '#markup' => $text,
      '#attached' => array(
        'library' => array(
          'core/drupal.dialog.ajax',
          'core/jquery.form',
        ),
      ),
    ];
  }
 
}?>

The form in the popup will look like this:

newsletter

I also connected a custom library from the drupalbook.libraries.yml file:

modules/custom/drupalbook/drupalbook.libraries.yml

multistep_form:
  css:
    css/multistep_form.css: {}
  js:
    scripts/multistep_form.js: {}
  dependencies:
    - core/jquery
    - core/jquery.once

It is necessary to stylize the form and connect the necessary jQuery plug-ins to it. Therefore, you need to create more files:

It is necessary to stylize the form and connect the necessary jQuery plug-ins to it. Therefore, you need to create more files:

modules/custom/drupalbook/css/multistep_form.css
modules/custom/drupalbook/scripts/multistep_form.js

Examples of code can be found at github:
https://github.com/levmyshkin/drupalbook8