Hi, Is it possible to wrap a form input inside it's label tag? I'd like to format the markup like this:

<div class="form-item form-type-textfield form-item-FirstName">
<label for="edit-firstname">First Name 
<span class="form-required" title="This field is required.">*</span>
<input type="text" id="edit-firstname" name="FirstName" value="" size="25" maxlength="37" class="form-text required" /></label>
</div>

With the label closing tag is after the end of the input.

I think I'll need to override the 'theme_form_element_label' function in the 'includes/form.inc' but I'm not sure how to go about it.

Comments

ooXei1sh’s picture

Got it:

<?php

/**
 * Implements hook_block_info().
 */
function myform_block_info() {
  $blocks['my-form'] = array(
    'info'  => t('Sah: my form'),
    'cache' => DRUPAL_NO_CACHE,
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function myform_block_view($delta='') {
  $block = array();
  switch ($delta) {
  case 'my-form':
    $block['subject'] = t('My Form');
    $block['content'] = drupal_get_form('myform');
    break;
  }
  return $block;
}

/**
 * Form builder
 */
function myform($form, &$form_state) {
  $form = array();
  $form['Question'] = array(
    '#type' => 'myform_question',
    '#title' => t('Is the sky blue?'),
  );
  return $form;
}

/**
 * Implements hook_element_info().
 */
function myform_element_info() {
  $types = array();
  $types['myform_question'] = array(
    '#input' => true,
    '#process' => array('myform_question_process'),
    '#autocomplete_path' => false,
  );
  return $types;
}

/**
 * Implements hook_process().
 */
function myform_question_process($element, &$form_state, $complete_form) {
  //$element['#tree'] = true;
  $element['QuestionYes'] = array(
    '#type' => 'radio',
    '#title' => 'Yes',
    '#title_display' => 'wraplabel',
    '#theme_wrappers' => array('myform_wraplabel'),
    '#attributes' => array('name' => 'Question'),
  );
  $element['QuestionNo'] = array(
    '#type' => 'radio',
    '#title' => 'No',
    '#title_display' => 'wraplabel',
    '#theme_wrappers' => array('myform_wraplabel'),
    '#attributes' => array('name' => 'Question'),
  );
  return $element;
}

/**
 * Implements hook_theme().
 */
function myform_theme(){
  return array(
    'myform_wraplabel' => array(
      'render element' => 'element',
    ),
  );
}

/**
 * theme radio inputs wrapped in label tags
 */
function theme_myform_wraplabel($variables) {  
  $element = $variables['element'];
  if (isset($element['#markup']) && !empty($element['#id'])) {
    $attributes['id'] = $element['#id'];
  }
  $attributes['class'] = array('form-item');
  if (!empty($element['#type'])) {
    $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-');
  }
  if (!empty($element['#name'])) {
    $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
  }
  if (!empty($element['#attributes']['disabled'])) {
    $attributes['class'][] = 'form-disabled';
  }
  $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
  switch ($element['#title_display']) {
    case 'wraplabel':
      $output .= '<label for="'. $element['#id'] .'">';
      $output .= ' ' . $prefix . $element['#children'];
      $output .= ' ' . $element['#title'] . ' ';
      $output .= '</lable>' . $suffix . "\n";
      break;
  }
  $output .= "</div>\n";
  return $output;
}
ooXei1sh’s picture

Ok, so the above code doesn't account for the form rebuild... thus if a radio was selected it will not be selected if the form needs to rebuild to show errors (like leaving a required field blank) but I found this which led me in the right direction:

http://www.encodez.com/blog/how-to-theme-radio-drupal.html

ooXei1sh’s picture

/**
 * Form builder
 */
function sahradio($form, &$form_state) {
  $form = array();
  $form['Required'] = array(
    '#type' => 'textfield',
    '#title' => t('Required'),
    '#size' => 25,
    '#maxlength' => 37,
    '#required' => true,
  );
  $form['Question'] = array(
    '#type' => 'radios',
    '#title' => t('Is the sky blue?'),
    '#theme' => 'my_radios',
    '#options' => array(
      'Yes' => 'Yes',
      'No' => 'No',
    ),
    '#required' => true,
    '#validated' => true, // suppress 'An illegal choice...'
  );
  $form['Submit'] = array(
    '#type' => 'submit',
    '#default_value' => 'Send',
  );
  return $form;
}

/**
 * Implements hook_theme().
 */
function sahradio_theme(){
  return array(
    'my_radios' => array(
      'render element' => 'element',
    ),
    'my_radio' => array(
      'render element' => 'element',
    ),
  );
}

/**
 * theme radios
 */
function theme_my_radios($variables) {
  $element = $variables['element'];  
  $keys = array_keys($element['#options']);
  $type = $element[$keys[0]]['#type'];
  
  // Add element #id for #type 'item'.
  if (isset($element['#markup']) && !empty($element['#id'])) {
    $attributes['id'] = $element['#id'];
  }
  // Add element's #type and #name as class to aid with JS/CSS selectors.
  $attributes['class'] = array('form-item');
  if (!empty($element['#type'])) {
    $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-');
  }
  if (!empty($element['#name'])) {
    $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
  }
  // Add a class for disabled elements to facilitate cross-browser styling.
  if (!empty($element['#attributes']['disabled'])) {
    $attributes['class'][] = 'form-disabled';
  }
  $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
  foreach ($keys as $key) {
    // Each radio is themed by calling our custom 'my_radio' theme function.
    $output .= theme('my_radio', $element[$key]);
  }
  $output .= '</div>';
  return $output;
}

/**
 * theme radio
 */
function theme_my_radio($variables) {
  $element = $variables['element'];
  _form_set_class($element, array('form-radio'));
  $output = '<input type="radio" ';
  $output .= 'id="' . $element['#id'] . '" ';
  $output .= 'name="' . $element['#name'] . '" ';
  $output .= 'value="' . $element['#return_value'] . '" ';
  $output .= (check_plain($element['#value']) == $element['#return_value']) ? ' checked="checked" ' : ' ';
  $output .= drupal_attributes($element['#attributes']) . ' />';
  if (!is_null($element['#title'])) {
    $output = '<label class="option" for="' . $element['#id'] . '">' . $output . ' ' . $element['#title'] . '</label>';
  }
  return $output;
}

/**
 * Form validate
 */
function sahradio_validate($form, &$form_state) {
  // radio button required handled here to avoid "An illegal choice has been detected." message
  $expected = array('Yes','No');
  if (isset($form_state['values']['Question']) && !in_array($form_state['values']['Question'], $expected)) {
    form_set_error('Question', t("Please answer the question: 'Is the sky blue?'"));
  }  
}