class DependentDropdown

Same name in other branches
  1. 4.0.x modules/ajax_example/src/Form/DependentDropdown.php \Drupal\ajax_example\Form\DependentDropdown

Re-populate a dropdown based on form state.

Hierarchy

Expanded class hierarchy of DependentDropdown

1 string reference to 'DependentDropdown'
ajax_example.routing.yml in modules/ajax_example/ajax_example.routing.yml
modules/ajax_example/ajax_example.routing.yml

File

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

Namespace

Drupal\ajax_example\Form
View source
class DependentDropdown extends FormBase {
    
    /**
     * {@inheritdoc}
     */
    public function getFormId() {
        return 'ajax_example_dependentdropdown';
    }
    
    /**
     * {@inheritdoc}
     *
     * The $nojs parameter is specified as a path parameter on the route.
     *
     * @see ajax_example.routing.yml
     */
    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;
    }
    
    /**
     * {@inheritdoc}
     */
    public function submitForm(array &$form, FormStateInterface $form_state) {
        $trigger = (string) $form_state->getTriggeringElement()['#value'];
        switch ($trigger) {
            case 'Submit':
                // Submit: We're done.
                $this->messenger()
                    ->addMessage($this->t('Your values have been submitted. Instrument family: @family, Instrument: @instrument', [
                    '@family' => $form_state->getValue('instrument_family_dropdown'),
                    '@instrument' => $form_state->getValue('instrument_dropdown'),
                ]));
                return;
        }
        // 'Choose' or anything else will cause rebuild of the form and present
        // it again.
        $form_state->setRebuild();
    }
    
    /**
     * Provide a new dropdown based on the AJAX call.
     *
     * This callback will occur *after* the form has been rebuilt by buildForm().
     * Since that's the case, the form should contain the right values for
     * instrument_dropdown.
     *
     * @param array $form
     *   An associative array containing the structure of the form.
     * @param \Drupal\Core\Form\FormStateInterface $form_state
     *   The current state of the form.
     *
     * @return array
     *   The portion of the render structure that will replace the
     *   instrument-dropdown-replace form element.
     */
    public function instrumentDropdownCallback(array $form, FormStateInterface $form_state) {
        return $form['instrument_fieldset_container'];
    }
    
    /**
     * Helper function to populate the first dropdown.
     *
     * This would normally be pulling data from the database.
     *
     * @return array
     *   Dropdown options.
     */
    public static function getFirstDropdownOptions() {
        return [
            'none' => 'none',
            'String' => 'String',
            'Woodwind' => 'Woodwind',
            'Brass' => 'Brass',
            'Percussion' => 'Percussion',
        ];
    }
    
    /**
     * Helper function to populate the second dropdown.
     *
     * This would normally be pulling data from the database.
     *
     * @param string $key
     *   This will determine which set of options is returned.
     *
     * @return array
     *   Dropdown options
     */
    public static function getSecondDropdownOptions($key = '') {
        switch ($key) {
            case 'String':
                $options = [
                    'Violin' => 'Violin',
                    'Viola' => 'Viola',
                    'Cello' => 'Cello',
                    'Double Bass' => 'Double Bass',
                ];
                break;
            case 'Woodwind':
                $options = [
                    'Flute' => 'Flute',
                    'Clarinet' => 'Clarinet',
                    'Oboe' => 'Oboe',
                    'Bassoon' => 'Bassoon',
                ];
                break;
            case 'Brass':
                $options = [
                    'Trumpet' => 'Trumpet',
                    'Trombone' => 'Trombone',
                    'French Horn' => 'French Horn',
                    'Euphonium' => 'Euphonium',
                ];
                break;
            case 'Percussion':
                $options = [
                    'Bass Drum' => 'Bass Drum',
                    'Timpani' => 'Timpani',
                    'Snare Drum' => 'Snare Drum',
                    'Tambourine' => 'Tambourine',
                ];
                break;
            default:
                $options = [
                    'none' => 'none',
                ];
                break;
        }
        return $options;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DependentDropdown::buildForm public function The $nojs parameter is specified as a path parameter on the route. Overrides FormInterface::buildForm
DependentDropdown::getFirstDropdownOptions public static function Helper function to populate the first dropdown.
DependentDropdown::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
DependentDropdown::getSecondDropdownOptions public static function Helper function to populate the second dropdown.
DependentDropdown::instrumentDropdownCallback public function Provide a new dropdown based on the AJAX call.
DependentDropdown::submitForm public function Form submission handler. Overrides FormInterface::submitForm
FormBase::$configFactory protected property The config factory. 3
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 3
FormBase::container private function Returns the service container.
FormBase::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create 105
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route.
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
FormBase::validateForm public function Form validation handler. Overrides FormInterface::validateForm 73
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 17
MessengerTrait::messenger public function Gets the messenger. 17
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a &#039;destination&#039; URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.