12.8.2. Miltistep popup форма в Drupal 8.

В этой статье мы продолжим разбираться с Form API в Drupal 8 и сделаем multistep форму. Мы уже создавали обычную конфигурационную форму для модуля, multistep форма создается похожим способом с использованием $form_state для хранения данных между шагами формы.

Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8

Для мультистеп формы нужно добавить класс формы:

/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 $formFormStateInterface $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 Terms and Conditions and Privacy Policy',
            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 &$formFormStateInterface $form_state) {
    return 
parent::validateForm($form$form_state);
  }
 
  
/**
   * {@block}
   */
  
public function submitForm(array &$formFormStateInterface $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 &$formFormStateInterface $form_state) {
    return 
$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'

Это создаст форму по адресу /multistep-form:

Чтобы эта форма открывалась в модальном попап окне нужно добавить блок или просто текст с определенным классом use-ajax и атрибутом data-dialog-type="modal", с ссылкой на страницу формы. Также нужно будет подключить библиотеки: core/drupal.dialog.ajax, core/jquery.form. Если сделать это через блок, то получится следующий код для кнопки открытия формы в попапе:

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',
        ),
      ),
    ];
  }
 
}
?>

Форма в попапе будет выглядеть следующим образом:

Также я подключил кастомную библиотеку из файла drupalbook.libraries.yml:

modules/custom/drupalbook/drupalbook.libraries.yml

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

Это нужно чтобы стилизовать форму и подключать к ней необходимые jQuery плагины. Поэтому нужно еще создать файлы:

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

Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8