Audience: developers creating forms programmatically

For many years now, I've been using #states in many of my pull requests that were around improving the admin user interface. I've found that hiding/showing form elements depending on various conditions, in many situations may result in a much better user experience for content editors and site builders using Backdrop.

There are many resources one can find around the internet that discuss this topic, but many users still seem to be confused, especially when it comes to using OR and XOR with #states (support for which has been added in Drupal core in March 2012, and which is what we've inherited in Backdrop as well). I believe that the cause for that may be the code snippet provided in that official change record, as it provides a (rather confusing, and possibly wrong in my opinion) single piece of code to explain both OR and XOR at the same time, within the same form element. So, instead of the official change record, my go-to article for revisiting/remembering how to properly use conditionals with #states in Drupal 7 has for many years been this Lullabot article from 2015 (thank you friendly robots! 🙏  ❤️  🤖  ).

This topic resurfaced in a recent code documentation task in the Backdrop core issue queue, and although the pseudo-code examples added to the API documentation are enough to explain things properly, some developers may prefer a real-code snippet + some visuals instead, so here it is 😉

$form['test_states'] = array(
  '#type' => 'checkboxes',
  '#title' => t('Test #states'),
  '#options' => backdrop_map_assoc(array('A', 'B')),
  '#description' => t('Click either or both checkboxes above, and see what happens to the states-controlled checkboxes below.'),
);
$form['test_and'] = array(
  '#type' => 'checkbox',
  '#title' => t('AND works (both A and B are checked)'),
  '#disabled' => TRUE,
  '#states' => array(
    'checked' => array(
      ':input[name="test_states[A]"]' => array('checked' => TRUE),
      ':input[name="test_states[B]"]' => array('checked' => TRUE),
    ),
  ),
);
$form['test_or'] = array(
  '#type' => 'checkbox',
  '#title' => t('OR works (at least one of A or B is checked)'),
  '#disabled' => TRUE,
  '#states' => array(
    'checked' => array(
      array(
        ':input[name="test_states[A]"]' => array('checked' => TRUE),
      ),
      'or', // This is the default operator, so it may be omitted.
      array(
        ':input[name="test_states[B]"]' => array('checked' => TRUE),
      ),
    ),
  ),
);
$form['test_xor'] = array(
  '#type' => 'checkbox',
  '#title' => t('XOR works (one of A or B is checked, but not both)'),
  '#disabled' => TRUE,
  '#states' => array(
    'checked' => array(
      array(
        ':input[name="test_states[A]"]' => array('checked' => TRUE),
      ),
      'xor',
      array(
        ':input[name="test_states[B]"]' => array('checked' => TRUE),
      ),
    ),
  ),
);

Try the above, and you'll end up with this test form, which demonstrates how AND/OR/XOR work with #states:

Happy coding! 🙂