Technical Tuesdays: #theme Form Elements into tables
Forms API (FAPI) is one of the coolest pieces of Drupal.
The power and flexibility of hook_form_alter has made it possible to change the behavior of a module, without actually modifying any code in the original module, and has created a whole new class of "helper" modules that enhance the capabilities of existing modules. But this article isn't about form_alter.
Another powerful feature of FAPI is the #theme attribute. It allows a module to provide the formatting of a form or form element. And it does this within the framework of theming, which also allows the theme to override the display. It's really sweet!
Before I dive into #theme, you should bookmark the forms_api_reference.html page. It shows all of the available form elements and form controls in a helpful table, with links to the full documentation.
This article is about one particular #theme function that I find really handy. I've recently started theming combo boxes and radio buttons in 2 and 3 column tables. This is relatively easy to do, so I thought I'd share the code.
First, you need the following function. I've put it within a
Then, in your form definition, attach #theme and optional #cols to the form element. For example:
It works for "comboboxes" and "radios" form elements. The default number of columns is 3, so you don't need to add #cols if you want three columns.
I use this code in a few places already. For example, it greatly simplifies the modules fieldset of the coder settings form, as you can see below. I've cut off the picture below, because it's pretty long. But it's three times longer without theming them as a a three column table!
if (!function_exists) so that it is safe to use in multiple modules. Ideally this should be part of includes/theme.inc in core, and I will try to get it accepted in the 7.x development cycle.
if (!function_exists('theme_cols')) {
/**
* Implement theme_cols to theme the radiobuttons and checkboxes form
* elements in a table column.
*/
function theme_cols($form) {
$total = 0;
$cols = isset($form['#cols']) ? $form['#cols'] : 3;
foreach ($form as $element_id => $element) {
if ($element_id[0] != '#') {
$total ++;
}
}
$total = (int) (($total % $cols) ? (($total + $cols - 1) / $cols) : ($total / $cols));
$pos = 0;
$rows = array();
foreach ($form as $element_id => $element) {
if ($element_id[0] != '#') {
$pos ++;
$row = $pos % $total;
$col = $pos / $total;
if (!isset($rows[$row])) {
$rows[$row] = array();
}
$rows[$row][$col] = drupal_render($element);
}
}
return theme('table', array(), $rows);
}
} $form['yourmodule_checkboxes'] = array(
'#type' => 'checkboxes',
'#options' => array('A', 'B', 'C', 'D', 'E', 'F', 'G'),
'#theme' => 'cols',
'#cols' => 4,
);



















Comments
First In a series
element_children
an issue for this
CSS columns
CSS tables
function theme_css_table($header, $rows, $attributes = array(), $caption = NULL) {
$output = '<div'. drupal_attributes($attributes) .">\n";
if (isset($caption)) {
$output .= '<caption>'. $caption ."</caption>\n";
}
// Format the table header:
if (count($header)) {
$ts = tablesort_init($header);
$output .= ' <div class="table_header">';
foreach ($header as $cell) {
$cell = tablesort_header($cell, $header, $ts);
$output .= _theme_table_cell($cell, TRUE);
}
$output .= " </div>\n";
}
// Format the table rows:
$output .= "<div class='table_body'>\n";
if (count($rows)) {
$flip = array('even' => 'odd', 'odd' => 'even');
$class = 'even';
foreach ($rows as $number => $row) {
$attributes = array();
// Check if we're dealing with a simple or complex row
if (isset($row['data'])) {
foreach ($row as $key => $value) {
if ($key == 'data') {
$cells = $value;
}
else {
$attributes[$key] = $value;
}
}
}
else {
$cells = $row;
}
// Add odd/even class
$class = $flip[$class];
if (isset($attributes['class'])) {
$attributes['class'] .= ' '. $class;
}
else {
$attributes['class'] = $class;
}
//add class to the row
$attributes['class'] .= ' table_row';
// count the number of cells in the row
// to get CSS width
$width = 100 / count($row);
// Build row
$output .= ' <div '. drupal_attributes($attributes) .'>';
$i = 0;
foreach ($cells as $cell) {
$cell = tablesort_cell($cell, $header, $ts, $i++);
$output .= '<div class="table_cell" style="float: left; width: '. $width .'%">'. $cell .'</div>';
}
$output .= " </div><br style='clear: both;' />\n";
}
}
$output .= "</div></div>\n";
return $output;
}
Ah, interesting. Take a
Patch for Drupal 7.x!
Don't forget hook_theme!
<?php/**
* Implementation of hook_theme().
*/
function MYMODULE_theme() {
return array(
'cols' => array(
'arguments' => array('form' => NULL),
),
);
}
?>
Arthur, I can't get this to