Skip to main content
ComputerMinds

Show elements with form #states when values do not match

I've previously written about dynamic forms in Drupal, using #states to show or hide input boxes depending on other inputs. Since then, Drupal 7 and 8 have both got the ability to combine conditions with OR and XOR operators. This makes it possible to apply changes when a form…

James WilliamsSenior Developer · · 2 min read

I've previously written about dynamic forms in Drupal, using #states to show or hide input boxes depending on other inputs. Since then, Drupal 7 and 8 have both got the ability to combine conditions with OR and XOR operators. This makes it possible to apply changes when a form element's value does not equal something, which is not obvious at all. It's easy to show something when a value does match. This example shows a text input box for specifying which colour is your favourite when you chose 'Other' from a dropdown list of favourite colours.

<code class="language-php">
$form['other_favorite'] = [
  '#type' => 'textfield',
  '#title' => t('Please specify...'),
  '#states' => [
    'visible' => [   // action to take.
      ':input[name="favorite_color"]' // element to evaluate condition on
        => ['value' => 'Other'],  // condition
    ],
  ],
];
</code>

But there's no 'opposite' of the value condition, e.g. to show an element when a text box has anything other than the original value in it. Using the 'XOR' condition (which only passes when some conditions are passed, but not all of them), you can combine the value condition with something that will always pass, in order to simulate it instead. Something like this, which shows a checkbox to confirm a change when you enter anything other than your previous favourite food:

<code class="language-php">
$form['confirm_change'] = [
  '#type' => 'checkbox',
  '#title' => t('Yes, I really have changed my mind'),
  // Use XOR so that current_pass element only shows if the email
  // address is not the existing value, and is visible. States cannot
  // check for a value not being something, but by using XOR with a
  // condition that should always be true (i.e. the latter one), this
  // will work. Note the extra level of arrays that is required for
  // using the XOR operator.
  '#states' => [
    'visible' => [
      [':input[name="favorite_food"]' => [
        'value' => $original_favorite_food,
      ]],
      'xor',
      [':input[name="favorite_food"]' => [
        // The checked property on a text input box will always come
        // out false.
        'checked' => FALSE,
      ]],
    ],
  ],
];
</code>

Hopefully the comments in that snippet make sense, but essentially it's about picking a second condition that will always pass, so that the combination only passes when the first condition fails. So in this case, whenever the value does not equal the original value. By the way, you can do much more than just show or hide form elements, for example, enabling or disabling - see the full list.

Written by

James Williams, Senior Developer

Meet the team