4

I am new to Drupal, so please be fair with me :)

I am trying to create a custom form using FAPI. The code is as follows :

$form['format_document'] = array(
  '#type' => 'radios',
  '#options' => array(
    'yes' => t('Yes'),
    'no' => t('No')
  ),
  '#title' => t('Should we format your document?:'),
  '#required' => TRUE,
  '#default_value' => 'no',

  '#suffix' => 'markup for the help image',

  '#states' => array(
    'visible' => array(
      ':input[name="select_service"]' => array(
        array('value' => '1'),
        array('value' => '2'),
        array('value' => '3'),
      ),
    ),
  ),
);

The #states work fine on the form element visibility; however the #suffix part is always visible. I can create a new form element with the same #states, but I have to do this on large number of elements. Is there something I am missing in the #states?

  • Well yes, that's exactly how it is designed to work. #suffix is rendered outside element's template, so #states have no way to know about it. Depending on what you need this 'Some custom HTML' for, solutions may be different. – Mołot Jul 11 '14 at 8:26
  • @Mołot Thank you for the prompt response. The custom HTML is a help image for the element. – Abhay Jul 11 '14 at 8:28
  • As @Mołot said, states refer only to the element, and the suffix isn't part of the element..you'll need to write custom javascript to accomplish this. – Clive Jul 11 '14 at 8:38
  • cool @Clive .. !!! thnx.. :) – Abhay Jul 11 '14 at 8:43
  • @Clive There is a way to have image inside wrapper, and it's compatible with states. So custom JS is just one of the options, not something OP needs to do. PHP solution is not worth implementing for one field, I freely admit, but for dozens or hundreds? I did it, and I found it worth doing in my case. – Mołot Jul 11 '14 at 9:13
3

You could put your form element with the suffix into a container forms element. The container can also have states:

$form['format_document_container'] = array(
  '#type' => 'container',
  '#states' => array(
    'visible' => array(
      ':input[name="select_service"]' => array(
        array('value' => '1'),
        array('value' => '2'),
        array('value' => '3'),
      ),
    ),
  ),
);

$form['format_document_container']['format_document'] = array(
  '#type' => 'radios',
  '#options' => array(
    'yes' => t('Yes'),
    'no' => t('No')
  ),
  '#title' => t('Should we format your document?:'),
  '#required' => TRUE,
  '#default_value' => 'no',

  '#suffix' => 'markup for the help image',
);
1

I faced similar issue. Problem is that #suffix is rendered outside element's template, so #states have no way to know about it.

My solution was to put image inside element. First, I registered it's theme function in my module:

function mymodule_theme() {
  return array(
    'preview_upload' => array(
      'render element' => 'element',
      'file' => 'mymodule.module',
    ),
  );
}

And then I defined this function as follows:

function theme_preview_upload($variables) {
  $element = $variables['element'];

  $attributes = array();
  if (isset($element['#id'])) {
    $attributes['id'] = $element['#id'];
  }
  if (!empty($element['#attributes']['class'])) {
    $attributes['class'] = (array) $element['#attributes']['class'];
  }
  $attributes['class'][] = 'form-managed-file';

  // This wrapper is required to apply JS behaviors and CSS styling.
  $output = '';
  $output .= '<div' . drupal_attributes($attributes) . '>';
  if (isset($element['#file']->uri)) {
    $output .= '<img src="' . image_style_url('thumbnail', $element['#file']->uri) . '" class="preview" />';
  }
  $output .= drupal_render_children($element);
  $output .= '</div>';
  return $output;
}

Now wherever I add '#theme' => 'preview_upload' to my '#type' => 'managed_file' elements, it provides me with preview image inside element's wrapper.

How I did it, exactly?

I searched api.drupal.org, found the original theme function for the element I wanted co change (theme_file_managed_file), copied it to new name and edited last lines, the $output .= block. Just do the same.

Remember that you can add arbitrary attributes to your form element. Nothing against setting:

'#theme' => 'with_help_image',
'#help_image' => 'http://example.com/sites/default/files/image.png',

and then using $element['#help_image'] in a way similar I was using $element['#file'], if your form element does not have this image "naturally".


Of course you may also forget #states and create custom JS code, that will be aware of your #suffix. Choice is ultimately yours, I would prefer theme change and built-in states, but it's just my own "I feel it's simpler in the long run" opinion, not a hard measurable fact.

  • Thank you so much for the answer. I found a easier way to do this actually using #field_suffix. Please see my answer. – Abhay Jul 11 '14 at 9:38
  • @Abhay #field_suffix is less universal. It is supposed to work only for checkbox, machine_name, password, password_confirm, radio, select, textarea and textfield. You are using radios, not radio, so it shouldn't, by the docs, to be possible to use #field_suffix. – Mołot Jul 11 '14 at 9:43
1

On searching through the documentation of Form API, I found that there is a shorter way of doing this.

I have used #field_suffix instead of '#suffix' and it works.. !!

The suffix that is being added this way actually becomes a part of the main div wrapping the element.

  • 2
    Good answer. Not universal one, as it only applies to very limited set of field types, but if it works, all right. Surprisingly, 'radios' are not on the list of types that accept #field_suffix, so it shouldn't work in your case. – Mołot Jul 11 '14 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.