function DependentDropdown::buildForm

Same name in other branches
  1. 8.x-1.x ajax_example/src/Form/DependentDropdown.php \Drupal\ajax_example\Form\DependentDropdown::buildForm()
  2. 4.0.x modules/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;
}