Tech Blog :: How to make a Views "exposed filter" dropdown appear as checkboxes


Sep 26 '09 2:02am
Tags

How to make a Views "exposed filter" dropdown appear as checkboxes

I'm using Views' "expose filters as a block" functionality in a custom search page. One of the filters is for content type, and users need to be able to select multiple options. Views can only generate multi-select lists as dropdowns, however, and the designs require checkboxes. Using a hook_form_alter to flip the #type from 'select' to 'checkboxes' fails because the form API structures those so differently. The views_filter_pack module claims to be able to convert from one to the other, but is extremely over-engineered for this purpose. Some searching around showed that other people had the same problem but no solutions seemed to work.

Then it dawned on me that I didn't need Drupal or Views to use checkboxes, I just needed the browser output to be rendered as checkboxes.

So I did a test in Firebug, rewriting the <select> as checkboxes, with the same name and value's, and it worked!

With that revelation, I made a hybrid of theme_select() and theme_checkboxes() called theme_select_as_checkboxes(), which takes a select element but renders checkboxes. Here's the code:

/**
 * hack to make exposed dropdowns appear as checkboxes
 * (can't do it w/ form_alter b/c whole structure is different)
 * just render the options directly as checkboxes, pass the values the same as the SELECT,
 * tricks the form api and views to think it's a dropdown
 * [registered w/ hook_theme, toggled w/ #theme in form_alter]
 */
function theme_select_as_checkboxes($element) {
  $output = '';
 
  $selected_options = (array) $element['#post'][$element['#name']];    // the selected keys from #options
 
  foreach($element['#options'] as $key=>$value) {
 
    $id = $element['#id'] . '-' . $key;      // custom
 
    // is this option selected?
    $selected = (array_search($key, $selected_options) !== false);    // (returns key or false)
 
    $checkbox = '<input type="checkbox" '
      . 'name="'. $element['#name'] . '[]' .'" '    // brackets are key -- just like select
        . 'id="'. $id .'" '
        . 'value="'. $key .'" '
        . ($selected ? ' checked="checked" ' : ' ')
        . drupal_attributes($element['#attributes']) .' />';
 
    $output .= '<label class="option" for="'. $id .'">' . $checkbox .' '. $value .'</label>' . "\n";
  }
  return theme_form_element($element, $output);    // wraps it neatly
}

To enable it, register the function in hook_theme() with 'select_as_checkboxes' = array( 'function' => 'theme_select_as_checkboxes'), and set the field you want, in my case 'type', to use the custom theme function: $form['type']['#theme'] = 'select_as_checkboxes';

And it works!
I find this a lot more sensible than converting the whole tree structure in a form_alter. The rendering itself is basically Drupal-standard, it just happens to mix up the types a little.

Hi Ben,

This looks like just the solution I'm looking for!

I haven't been using Drupal long. Please can you explain a little clearer how and where to "register the function in hook_theme()" and also to "...set the field you want...to use the custom theme function".

Thanks,

(another) Ben

(You didn't leave an email so I'm not sure if you'll see this response...)

In your custom module, create a function MODULE_theme() (see the API doc), which returns an array containing [at least] one element, 'select_as_checkboxes', as described.

You set the field in a hook_form_alter, (checking for the $form_id or $form['#id'] to control its scope), setting #theme as described.

Let me know if that makes it any clearer.

Perfect solution!

Post new comment

Don't bother putting in spam links. They'll be set to rel=nofollow and will be removed and reported as spam shortly after submitting.

The content of this field is kept private and will not be shown publicly.
CAPTCHA
Are you human?