You are here

public function DependentDropdown::buildForm in Examples for Developers 3.x

Same name and namespace in other branches
  1. 8 ajax_example/src/Form/DependentDropdown.php \Drupal\ajax_example\Form\DependentDropdown::buildForm()

The $nojs parameter is specified as a path parameter on the route.

Overrides FormInterface::buildForm

See also

ajax_example.routing.yml

File

modules/ajax_example/src/Form/DependentDropdown.php, line 28

Class

DependentDropdown
Re-populate a dropdown based on form state.

Namespace

Drupal\ajax_example\Form

Code

public function buildForm(array $form, FormStateInterface $form_state, $nojs = NULL) {

  // Add our CSS and tiny JS to hide things when they should be hidden.
  $form['#attached']['library'][] = 'ajax_example/ajax_example.library';

  // Explanatory text with helpful links.
  $form['info'] = [
    '#markup' => $this
      ->t('<p>Like other examples in this module, this form has a path that
          can be modified with /nojs to simulate its behavior without JavaScript.
        </p><ul>
        <li>@try_it_without_ajax</li>
        <li>@try_it_with_ajax</li>
      </ul>', [
      '@try_it_without_ajax' => Link::createFromRoute($this
        ->t('Try it without AJAX'), 'ajax_example.dependent_dropdown', [
        'nojs' => 'nojs',
      ])
        ->toString(),
      '@try_it_with_ajax' => Link::createFromRoute($this
        ->t('Try it with AJAX'), 'ajax_example.dependent_dropdown')
        ->toString(),
    ]),
  ];

  // Our first dropdown lets us select a family of instruments: String,
  // Woodwind, Brass, or Percussion.
  $instrument_family_options = static::getFirstDropdownOptions();

  // When the AJAX request occurs, this form will be build in order to process
  // form state before the AJAX callback is called. We can use this
  // opportunity to populate the form as we wish based on the changes to the
  // form that caused the AJAX request. If the user caused the AJAX request,
  // then it would have been setting a value for instrument_family_options.
  // So if there's a value in that dropdown before we build it here, we grab
  // it's value to help us build the specific instrument dropdown. Otherwise
  // we can just use the value of the first item as the default value.
  if (empty($form_state
    ->getValue('instrument_family_dropdown'))) {

    // Use a default value.
    $selected_family = key($instrument_family_options);
  }
  else {

    // Get the value if it already exists.
    $selected_family = $form_state
      ->getValue('instrument_family_dropdown');
  }
  $form['instrument_family_fieldset'] = [
    '#type' => 'fieldset',
    '#title' => $this
      ->t('Choose an instrument family'),
  ];
  $form['instrument_family_fieldset']['instrument_family_dropdown'] = [
    '#type' => 'select',
    '#title' => $this
      ->t('Instrument Type'),
    '#options' => $instrument_family_options,
    '#default_value' => $selected_family,
    // Bind an ajax callback to the change event (which is the default for the
    // select form type) of the first dropdown. It will replace the second
    // dropdown when rebuilt.
    '#ajax' => [
      // When 'event' occurs, Drupal will perform an ajax request in the
      // background. Usually the default value is sufficient (eg. change for
      // select elements), but valid values include any jQuery event,
      // most notably 'mousedown', 'blur', and 'submit'.
      'callback' => '::instrumentDropdownCallback',
      'wrapper' => 'instrument-fieldset-container',
    ],
  ];

  // Since we don't know if the user has js or not, we always need to output
  // this element, then hide it with with css if javascript is enabled.
  $form['instrument_family_fieldset']['choose_family'] = [
    '#type' => 'submit',
    '#value' => $this
      ->t('Choose'),
    '#attributes' => [
      'class' => [
        'ajax-example-hide',
        'ajax-example-inline',
      ],
    ],
  ];

  // We are using the path parameter $nojs to signal when to simulate the
  // the user turning off JavaScript. We'll remove all the AJAX elements. This
  // is not required, and is here so that we can demonstrate a graceful
  // fallback without having to turn off JavaScript.
  if ($nojs == 'nojs') {

    // Removing the #ajax element tells the system not to use AJAX.
    unset($form['instrument_family_fieldset']['instrument_family_dropdown']['#ajax']);

    // Removing the ajax-example-hide class from the Choose button ensures
    // that our JavaScript won't hide it.
    unset($form['instrument_family_fieldset']['choose_family']['#attributes']);
  }

  // Since we're managing state for this whole fieldset (both the dropdown
  // and enabling the Submit button), we want to replace the whole thing
  // on AJAX requests. That's why we put it in this container.
  $form['instrument_fieldset_container'] = [
    '#type' => 'container',
    '#attributes' => [
      'id' => 'instrument-fieldset-container',
    ],
  ];

  // Build the instrument field set.
  $form['instrument_fieldset_container']['instrument_fieldset'] = [
    '#type' => 'fieldset',
    '#title' => $this
      ->t('Choose an instrument'),
  ];
  $form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown'] = [
    '#type' => 'select',
    '#title' => $instrument_family_options[$selected_family] . ' ' . $this
      ->t('Instruments'),
    // When the form is rebuilt during ajax processing, the $selected_family
    // variable will now have the new value and so the options will change.
    '#options' => static::getSecondDropdownOptions($selected_family),
    '#default_value' => !empty($form_state
      ->getValue('instrument_dropdown')) ? $form_state
      ->getValue('instrument_dropdown') : '',
  ];
  $form['instrument_fieldset_container']['instrument_fieldset']['submit'] = [
    '#type' => 'submit',
    '#value' => $this
      ->t('Submit'),
  ];

  // We might normally use #state to disable the instrument fields based on
  // the instrument family fields. But since the premise is that we don't have
  // JavaScript running, #state won't work either. We have to set up the state
  // of the instrument fieldset here, based on the selected instrument family.
  if ($selected_family == 'none') {
    $form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown']['#title'] = $this
      ->t('You must choose an instrument family.');
    $form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown']['#disabled'] = TRUE;
    $form['instrument_fieldset_container']['instrument_fieldset']['submit']['#disabled'] = TRUE;
  }
  else {
    $form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown']['#disabled'] = FALSE;
    $form['instrument_fieldset_container']['instrument_fieldset']['submit']['#disabled'] = FALSE;
  }
  return $form;
}