I'm creating some additional functionality for a module and as a part of that I'd like to use the form state API to conditionally show and hide some elements. I've been able to toggle the visibility of a textfield using a select field but when i attempt to do the same for a datetime or table select field, it doesn't work and I'm sure why. Here's my code:

$form['duration'] = [
  '#type' => 'select',
  '#title' => t('Lifetime'),
  '#options' => [
    60 => t('@days Days', ['@days' => 1]),
    172800 => t('@days Days', ['@days' => 2]),
    345600 => t('@days Days', ['@days' => 4]),
    604800 => t('@days Days', ['@days' => 7]),
    1209600 => t('@days Days', ['@days' => 14]),
    -1 => t('Unlimited'),
    'other' => t('Custom'),
  ],
  '#default_value' => '',
  '#attributes' => [
    'id' => 'edit-duration',
  ],
];

// Elements inside container are hidden by default and are shown when other
// is selected from the select list above.
$form['container'] = [
  '#type' => 'container',
  '#states' => [
    'visible' => [
      '[name="duration"]' => ['value' => 'other'],
    ],
  ],
];

// todo:// Add today's date as default if one doesn't exist in config.
$form['container']['expiration'] = [
  '#type' => 'datetime',
  '#title' => $this->t('Content expiration'),
  '#default_value' => DrupalDateTime::createFromTimestamp($config->get('custom_date')),
];

$bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo('node');

$options = $defaults = [];
foreach ($bundles as $bundle_id => $bundle) {
  $options[$bundle_id] = [
    'title' => ['data' => ['#title' => $bundle['label']]],
    'type' => $bundle['label'],
  ];
}

$selected_types = $config->get('selected_content_types');

foreach ($selected_types as $key => $type) {
  $defaults[$type] = TRUE;
}

if (!empty($options)) {
  $form['container']['bundles'] = [
    '#type' => 'tableselect',
    '#header' => [
      'type' => 'Content types',
    ],
    '#options' => $options,
    '#default_value' => $defaults,
    '#attributes' => [
      'class' => [
        'no-highlight'
      ],
    ],
  ];

  $form['bundles']['#attributes']['class'][] = 'content-type-table';
}

any help/tips would be greatly appreciated, thanks in advance !

UPDATE: I've now update code so that both the elements i want to hide are inside a container and I've added the states visible property on the container as per wombatbuddy's comment and it works. 

Comments

jaypan’s picture

It's almost always a bad idea to set the name attribute in Drupal forms. It leads to unexpected consequences.

Leave the name as it is, and either use a class on the element for your selector, or use the default name set by Drupal.

Contact me to contract me for D7 -> D10/11 migrations.

lincoln-batsirayi’s picture

Oh, Didn't know that, I've updated it to use the default name. Thank you. Unfortunately the issue persists...

jaypan’s picture

You need to update the code in the original post then.

Contact me to contract me for D7 -> D10/11 migrations.

lincoln-batsirayi’s picture

Oh yh of course, my bad. Done.

jaypan’s picture

Have you verified this element exists:

[name="duration"]

Contact me to contract me for D7 -> D10/11 migrations.

lincoln-batsirayi’s picture

Yes i have and it does indeed it's the one generated by drupal from the form element creation of the duration select element.

wombatbuddy’s picture

Just put them in a container, like this: 

$form['container'] = [
  '#type' => 'container',
  '#states' => [
    'visible' => [
      '[name="duration"]' => ['value' => 'other'],
    ],
  ],
];

$form['container']['expiration'] = [
 '#type' => 'datetime',
 '#title' => $this->t('Content expiration'),
 '#default_value' => DrupalDateTime::createFromTimestamp($config->get('custom_date')),
];
lincoln-batsirayi’s picture

Omg yes, that worked ! Thank you so much ! Now to understand the issue, do states only work certain elements?

jaypan’s picture

Well, they only don't work on a few elements. It depends on how the template for that element outputs its attributes. I'd run into this issue with other element types in the past (which I don't recall off the top of my head, and may have even been a datettime), but it works for nearly all element types.

Contact me to contract me for D7 -> D10/11 migrations.