- 🇨🇦Canada sagesolutions
I've also came across this scenario and found what works (and what doesn't). I'm currently on Drupal 10.0.3 and PHP 8.1
I needed to create a block with a form that submits to an external url. Its a simple form and only has a select box and submit button.
In the build() function of my block, I added the following:
$form['location'] = [ '#title' => $this->t('Airport'), '#type' => 'select', '#options' => [ 'calgary' => 'Calgary', 'edmonton' => 'Edmonton', ], '#default_value' => $location, '#attributes' => [ 'name' => 'location', ], ]; return [ '#theme' => 'reservation_form', '#form' => $form, ];
The select box renders properly, but the default value is not set.
So, instead I thought to create a Form and then load the form into the block and see if that works. And it did!
So I created the form class extending FormBase and added the select field to the buildForm() method
class ReservationForm extends FormBase { public function buildForm(array $form, FormStateInterface $form_state) { $form['location'] = [ '#title' => $this->t('Airport'), '#type' => 'select', '#options' => [ 'calgary' => 'Calgary', 'edmonton' => 'Edmonton', ], '#default_value' => $location, '#attributes' => [ 'name' => 'location', ], ]; return $form; } ... }
And then in my block, I embedded the form via
class ReservationFormBlock extends BlockBase { public function build(): array { $form = \Drupal::formBuilder() ->getForm('Drupal\my_module\Form\ReservationForm'); return [ '#theme' => 'reservation_form', '#form' => $form, ]; }
Calling the form this way set the default value on the select field.
- 🇨🇦Canada phjou Vancouver 🇨🇦 🇪🇺
Same issue here,
If you create a renderable array of a select and use the render service to render it, you will get the select but default_value is just ignore. It definitely doesn't make sense. It should work in both context, when used in a proper drupal form (works already) but also if you render an isolated select.
- 🇬🇧United Kingdom johan.gant Belfast, UK
I've been scratching my head about this for a while today. I have a views exposed form element as a select element, with about 10 options and should only take one value. I can use #value to pre-populate the initial value but this is then fixed and can't be changed by the user. If I try to use #default_value to indicate a starting value/pre-selection it's completely ignored on this element type.
I'm pretty sure I've used #default_value as an attribute in Drupal 7 and 8. The Select.php class docs state:
* - #default_value: Must be NULL or not set in case there is no value for the * element yet, in which case a first default option is inserted by default. * Whether this first option is a valid option depends on whether the field * is #required or not.
If I try to adjust #empty_value the form tells me 'The submitted value is not allowed'.
Entirely probable that I'm applying the wrong combination of properties (somehow) but I'm confused how #default_value can't be applied on this element (string for single value, array for multiple values), when other elements accept this property in a consistent manner.
What can be done to clarify the appropriate way to use the Select element via the class docs, or is this a bug?
- 🇮🇳India Akhil Babu Chengannur
I was able to reproduce this issue.
If I create a form with a select field and use \Drupal::formBuilder()->getForm() to render the form and return it from a controller, then the default value of the select element gets applied.
But if I do this in controller
$form['location'] = [ '#title' => $this->t('Airport'), '#type' => 'select', '#options' => [ 'calgary' => 'Calgary', 'edmonton' => 'Edmonton', ], '#default_value' => 'edmonton', ]; return $form;
Then the element gets rendered, but default value is ignored.
Options in the select element are processed by form_select_options function.
Every option in the select element will have a 'selected' attribute. This attribute should be set to true for the option that is selected as the default value. This is the part of the code that handles this logic.
$value_valid = \array_key_exists('#value', $element); // // if ($value_valid && (!$value_is_array && (string) $element['#value'] === $key || $value_is_array && in_array($key, $element['#value']) || $empty_choice)) { $option['selected'] = TRUE; }
The '#value' attribute of the element determies if an option should be selected by default or not.
When the form is rendered through form API, \Drupal\Core\Form\FormBuilder::handleInputElement() sets the '#value' attribute to the element. The function goes through several checks to get the #value. If #value is still not set, then #default_value is set as #value.
// Final catch. If we haven't set a value yet, use the explicit default // value. Avoid image buttons (which come with garbage value), so we // only get value for the button actually clicked. if (!isset($element['#value']) && empty($element['#has_garbage_value'])) { $element['#value'] = $element['#default_value'] ?? ''; } }
This entire process is skipped when form is rendered directly using render service. As a result, '#value' is not set and eventually default value is not highlightet in frontend.
form_select_options is called from template_preprocess_select. I have added a check to add '#value' attribute if its not set. This fixes the issue and default value get's selected when element is rendered. But not sure if this is the proper way to address this problem.
- Merge request !5706Issues/2895887: Set #value attribute to select element if not present → (Open) created by Akhil Babu
- 🇬🇧United Kingdom johan.gant Belfast, UK
For what it's worth, I found that I could work around this with something like:
$form['foo'] = [ '#type' => 'select', '#required' => FALSE, '#empty_value' => FALSE, '#sort_options' => TRUE, '#options' => [... list of key/value options ...], ]; combined with something like this in a form_alter hook: $foo_value = \Drupal::request()->query->get('foo'); if (empty($foo_value) { $form['foo']['#value'] = 'key_value_from_one_of_the_options', }
Which essentially means if there's no query string value for the filter, it sets a default value.
Wouldn't it be a lot handier to just set a #default_value attribute on the render element and internalise any other complications around that to the Select render element class? I appreciate I might not fully understand the existing reasons so would be more than happy if someone who can explain it in more simple terms could do that and enlighten me.
- 🇮🇹Italy realgiucas
It's a pretty big and annoying bug.
explanations would be appreciated.