0

I'm trying to create a form in Drupal 10 that will show an field that is both visible and required depending on the selection of a radio input that comes before it. The visible state seems to work well but for some reason the required and/or optional states do not seem to work on select fields.

public function buildForm(array $form, FormStateInterface $form_state) {
  $form['field_1'] = [
    '#type' => 'radios',
    '#title' => $this->t('Field 1'),
    '#options' => [
        'one' => $this->t('One'),
        'two' => $this->t('Two')
      ],
      '#required' => TRUE,
  ];

  // The field will become invisible but is still required.
  $form['field_2'] = [
      '#type' => 'select',
      '#title' => $this->t('Field 2'),
      '#options' => [
        'A' => $this->t('A'),
        'B' => $this->t('B'),
        'C' => $this->t('C'),
        'D' => $this->t('D'),
      ],
      '#required' => true,
      '#states' => [
        'visible' => [
          ':input[name="field_1"]' => ['value' => 'two']
        ],
        'optional' => [
          ':input[name="field_1"]' => ['value' => 'one']
        ],
      ],
    ];

  $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

  return $form;
}

When inspecting the HTML you can see that the code for the select is invisible but the required attribute is still present so when you hit submit the form will throw an error. This only happens for select field types as changing the type to textfield/radios seems to work without issues.

1
  • Unless something changed recently, #required is still server-side validation, and #states are client-side - they don't mix. The usual approach for your requirement is to add #ajax to the first select, and change the #required status of the second select in the builder based on the changed form state
    – Clive
    Commented Nov 7, 2023 at 14:54

1 Answer 1

0

You have to tweak code like this:

  $form['field_2'] = [
        '#type' => 'select',
        '#title' => $this->t('Field 2'),
        '#options' => [
          'A' => $this->t('A'),
          'B' => $this->t('B'),
          'C' => $this->t('C'),
          'D' => $this->t('D'),
        ],
        '#states' => [
          'visible' => [
            ':input[name="field_1"]' => ['value' => 'two']
          ],
          'required' => [
            ':input[name="field_1"]' => ['value' => 'two']
          ],
        ],
  ];

I passed the required attribute inside state properly & removed the required attribute from the field definition

Note: Basically if you want to attach the required attribute to field w.r.t to Form state then you should attach with state property, if attach outside the state property then it 'll always bind to form element & 'll always fire

Additional Info: Since states are driven by JavaScript only, it is important to understand that all states are applied on presentation only, none of the states force any server-side logic, and that they will not be applied for site visitors without JavaScript support

5
  • Does this still make the field required when javascript is disabled?
    – Clive
    Commented Nov 7, 2023 at 15:01
  • Strange, this does seem to work however the label is missing the (*Required) <span> that gets added to the label so I assumed it wasn't working before.
    – blu
    Commented Nov 7, 2023 at 15:05
  • That's because it's only "required" on the front-end, not the back-end (the back-end being the one that produces the markup). If you use this approach be aware that users who disable javascript will still be able to submit the form, this is more "pre-validation" than actual validation of the data
    – Clive
    Commented Nov 7, 2023 at 15:07
  • i will still have the validateForm() hook setup to catch this. Disabling JavaScript seems to break all '#states' functionality.
    – blu
    Commented Nov 7, 2023 at 15:14
  • Agree with clive , on disabling javascript validation as well whole form state api not work & ans provided w.r.t to form state API context. Commented Nov 7, 2023 at 15:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.