Form States in Drupal
The Forms API in drupal is one of the most complex things that I saw in my life, it has a lot of features that you in your normal life will never use it but they are there. Since the API is huge, the documentation never is enough and there is a lot of dark areas that are hard to understand. One of those areas are Form States.
A usual requirement is “When I select this I want to see that, if not that must be hidden”, a typical requirement for dynamic forms. As you will probably know you’ll try to add this dynamism with jQuery selecting elements and hide/showing depending on values; I did it in this way for a long time.
A few months ago Luis suggested me to try with Form States so I started to look about this.
The form API provides a mecanishm to handle the state of the form, and show, hide, enable, disable, bla, bla, bla, the elements in the form depending on the user actions over the form. In the essence what it does is use jQuery in the background to trigger the actions, but the cool thing is that you don’t need to write any javascript code, you just need to define the actions in your form with PHP code and Drupal will generate the JavaScript code for you.
In order to apply this states you need to add another entry into you form elements with the key #states and define into this entry the conditions.
The most important thing that you must know is that the condition are defined with this syntax.
'#states' => array( 'STATE' => array( JQUERY_SELECTOR => REMOTE_CONDITIONS, JQUERY_SELECTOR => REMOTE_CONDITIONS, ... ), ),
Each form field consist of states and remote conditions that define the properties of the field. A state is a property that can be applied to a form element (i.e. enabled, disabled, checked, unchecked), while a remote condition is the state of an element to trigger a change in different element. All the available states and remote conditions are defined in the drupal_process_states().
I will show you a sample about how to use states in a node form, since there is a lot of sample in the internet for custom forms.
In my case, I have a content type that have a select field that define a type, depending on the type we have to fill some values in different fields, so when the user selects a value I want to show the related inputs with that value and hide the others. Since this is a content type I need to alter the node form for this content type so I’ll use a form_alter.
Here is the code:
/** * Implements hook_form_alter. */ function my_module_form_alter(&$form, &$form_state, $form_id) { //Shows/Hides elements depending in type selection. if ($form_id == 'my_ct_node_form') { $form['field_value_one']['#states'] = array( 'visible' => array( array( 'select[name="field_type"]' => array('value' => 'one'), ), ) ); $form['field_value_two']['#states'] = array( 'visible' => array( array( 'select[name="field_type"]' => array('value' => 'two'), ), ) ); $form['field_value_three']['#states'] = array( 'visible' => array( array( 'select[name="field_type"]' => array('value' => 'three'), ), ) ); } }
In this case, when the user selects the value one in the field type, it will show the field_value_one in the form and will hide the others.
Of course there is several other states to use, it will cover all most all escenarios but they not cover all. For more complex things the api support logical operators to combine several states. Also there is possible to inject pieces of JavaScript code, I’ll recommend to use that wisely.
Be aware that in some samples the selector is written like “:input[name=fff]”, :input is the jQuery shortcut selector to select any input, but keep in mind that this is a jQuery selector, so you’ll need to write a valid jQuery selector to make it work properly.
Here you have some links to look more about form states
https://api.drupal.org/api/drupal/includes%21common.inc/function/drupal_process_states/7
https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/7#states