blog-banner

Drupal 7 Form API - Using #states on #markup Field

    Drupal 7 Form States

    An essential feature of a dynamic site would be to let its users add some content to it. Often this is done using HTML forms. Drupal offers a rich and relatively simple way to use API for building forms for its Developers. In Drupal parlance, it is referred to as Form API (or FAPI).

    The beauty of this is, that developers can create the forms with PHP itself. Essentially using array keys and values (thanks to Drupal coding standards for offering a more readable structure). That is developers don't have to mess with both HTML (for creating / structuring) and PHP (for validation/response handling). With just PHP code we can get the job done.

    Drupal 7 adds more spice to this Form API stuff. Yes, I'm talking about the newly added #states property. At times it is needed to make a field change its behavior based on the value its predecessor field takes, #states have been added in this connection. Without #states and earlier to Drupal 7, the common approach for this case would be to add a JavaScript file on form load. The JS file will have a jQuery snippet to do the needful alteration. But with #states the custom JS code is no longer needed. An additional array keys/values pair would do the needful.

    In one of my recent works, I had to show a help text (#markup text) as a part of the sign-up form based on the value chosen for the plan field.

    It was noticed that #states are having no effect on #markup field. The code I thought would work is below,

    <?php
    $form['plan'] = array(
      '#title' => t('Select a Plan'),
      '#type' => 'radios',
      '#options' => $plans,
      '#default_value' => $default_plan,
    );
    foreach ($plans as $nid => $plan) {    
      $form['plan_feature_' . $nid]['markup'] = array(
        '#markup' => kf_support_features($nid),
        '#states' => array(
          'visible' => array(
            ':input[name=plan]' => array('value' => $nid),
         ),
      );
    }

    Essentially I need to show a specific HTML text block based on the value chosen for the plan field. Note the #states in the second field. It just didn't work the way it was expected. Looks like a limitation in Form API and core web developers might have done this for some obvious reasons (could be because there is no HTML id attribute for the markup field).

    To workaround this case, a special wrapper field type is there in Form API. Referred to by the name 'container'.

    Actually, this was explained in the Form API page comment with a sample code. Thanks to becw for the same. Accidentally searching for '#markup' helped me locate this tip. Source https://api.drupal.org/comment/11049#comment-11049

    After updating the code to use the 'container' element, it worked as expected. The revised code is below,

    <?php
    $form['plan'] = array(
      '#title' => t('Select a Plan'),
      '#type' => 'radios',
      '#options' => $plans,
      '#default_value' => $default_plan,
    );
    foreach ($plans as $nid => $plan) {    
      $form['plan_feature_' . $nid] = array(
        '#type' => 'container',
        '#states' => array(
          'visible' => array(
            ':input[name=plan]' => array('value' => $nid),
          ),
        ),
      );
      $form['code_feature_matrix_' . $nid]['markup'] = array(
        '#markup' => kf_support_features($nid),
      );
    }

    I hope this would help others as well.

    Get awesome tech content in your inbox