Allow #states to work even for elements without the form_element theme wrapper

Created on 2 June 2022, almost 2 years ago
Updated 16 November 2023, 7 months ago

Problem/Motivation

When a form contains a table, that table does not properly handle the #states property. As a workaround, the table can be wrapped in a container element type, but that shouldn't be necessary.

<!--break-->

In digging into the code, it appears that FormHelper::processStates() gets invoked for all render elements, including tables, and the <table> HTML element is rendered with a data-drupal-states HTML attribute. In addition, states.js does process the behavior and sets up all the triggers. It seems like the issue is that states.js uses jQuery to find the nearest '.js-form-item, .js-form-submit, .js-form-wrapper' wrapper around the table, which it does not find. This causes the behavior not to work.

Steps to reproduce

  1. Create a form like this in a custom module:
    
    namespace Drupal\my_module\Form;
    
    use Drupal\Core\Form\FormBase;
    use Drupal\Core\Form\FormStateInterface;
    
    class SomeForm extends FormBase {
    
      public function getFormId(): string {
        return 'repro_form';
      }
    
      public function buildForm(array $form,
                                FormStateInterface $form_state): array {
        $form['required'] = [
          '#type'          => 'checkbox',
          '#title'         => $this->t('Check me'),
          '#default_value' => FALSE,
        ];
    
        $form['some_textfield'] = [
          '#type'  => 'textfield',
          '#title' => $this->t('Some field'),
          '#states' => [
            'invisible' => [
              ':input[name="required"]' => [
                'checked' => TRUE,
              ],
            ],
          ],
        ];
    
        $contacts_table = $this->buildContactsTable();
    
        $contacts_table['#states'] = [
          'invisible' => [
            ':input[name="required"]' => [
              'checked' => TRUE,
            ],
          ],
        ];
    
        $form['contacts'] = $contacts_table;
    
        $form['contacts_wrapper'] = [
          '#type'   => 'container',
          '#states' => [
            'invisible' => [
              ':input[name="required"]' => [
                'checked' => TRUE,
              ],
            ],
          ],
          'contacts' => $contacts_table,
        ];
    
        return $form;
      }
    
      protected function buildContactsTable(): array {
        $table = [
          '#type'    => 'table',
          '#caption' => $this->t('Sample Table'),
          '#header'  => [
            $this->t('Name'),
            $this->t('Phone'),
          ],
        ];
    
        for ($i = 1; $i <= 4; $i++) {
          $table[$i]['name'] = [
            '#type'          => 'textfield',
            '#title'         => $this->t('Name'),
            '#title_display' => 'invisible',
          ];
    
          $table[$i]['phone'] = [
            '#type'          => 'tel',
            '#title'         => $this->t('Phone'),
            '#title_display' => 'invisible',
          ];
        }
    
        return $table;
      }
    
      public function submitForm(array &$form, FormStateInterface $form_state) {
        // Do nothing for this sample.
      }
    
    }
    
  2. Create a routing file in the custom module for the form:
    some.form:
      path: '/admin/some-form'
      defaults:
        _form: '\Drupal\my_module\Form\SomeForm'
        _title: 'Demonstrate bugs'
      requirements:
        _access: 'TRUE'
    
  3. Install the module or clear caches (if module is already installed).
  4. Visit /admin/some-form
  5. Place a checkmark in the "Check me" box.

All the form elements except the checkbox should hide, but the unwrapped table does not.

Proposed resolution

Either tables should be wrapped automatically by some class that states.js recognizes, or states.js should not require a wrapper element to do its work.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

✨ Feature request
Status

Active

Version

11.0 πŸ”₯

Component
FormΒ  β†’

Last updated about 1 hour ago

Created by

πŸ‡ΊπŸ‡ΈUnited States GuyPaddock

Live updates comments and jobs are added and updated live.
  • JavaScript

    Affects the content, performance, or handling of Javascript.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    I think you can make this work in a specific use case by adding 'form_element' to $element['#theme_wrappers'] where $element is a table.

    Moving to a feature request.

Production build 0.69.0 2024