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

Ben Buckman's picture

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.

Comments

Erik's picture

Hello,
Great read and exactly what I need. However, I have trouble implementing due to a lack of deep drupal php knowledge.

1) I included your funtion in template.php
2) I made an addition to my ZEN hook_theme

function SET_theme(&$existing, $type, $theme, $path) {
$hooks = zen_theme($existing, $type, $theme, $path);
$hooks['comment_form'] = array(
'arguments' => array('form' => NULL));
$hooks['select_as_checkboxes'] = array(
'function' => 'theme_select_as_checkboxes' );

return $hooks;
}

3) Where should i include the following line?
$form['type']['#theme'] = 'select_as_checkboxes';

Ben Rollins's picture

Hi Ben,

Looks like a great and customisable workaround - seems like you could also rewrite it for radio buttons etc...

But I'm having some trouble following where things go too - I am assuming you register the function in form.inc (yes?), but I am confused about where to find hook_theme and where to set the custom theme to the field.

I have another question, which is - will this work for CCK fields?

Thanks for doing the work, by the way - the views filter pack module does some weird weird things to the SQL queries and doesn't seem to function at all...

Ben

Peter Robot's picture

Erik,

I just implemented this with good success. Was more customizable than trying to modify the select items in a custom module.

The "$form['type']['#theme'] = 'select_as_checkboxes';" goes in a custom module that you'll have to create in a form_alter hook.

I spent 1.5 days trying to change the select to checkboxes in my custom module.

I spent 1.5 hours to finish this with Ben's method.

Peter Robot's picture

Erik,

I just implemented this with good success. Was more customizable than trying to modify the select items in a custom module.

The "$form['type']['#theme'] = 'select_as_checkboxes';" goes in a custom module that you'll have to create in a form_alter hook.

I spent 1.5 days trying to change the select to checkboxes in my custom module.

I spent 1.5 hours to finish this with Ben's method.

Peter B's picture

Erik,

I just implemented this with good success. Was more customizable than trying to modify the select items in a custom module.

The "$form['type']['#theme'] = 'select_as_checkboxes';" goes in a custom module that you'll have to create in a form_alter hook.

I spent 1.5 days trying to change the select to checkboxes in my custom module.

I spent 1.5 hours to finish this with Ben's method.

Peter B's picture

Erik,

I just implemented this with good success. Was more customizable than trying to modify the select items in a custom module.

The "$form['type']['#theme'] = 'select_as_checkboxes';" goes in a custom module that you'll have to create in a form_alter hook.

I spent 1.5 days trying to change the select to checkboxes in my custom module.

I spent 1.5 hours to finish this with Ben's method.

Peter B's picture

Erik,

I just implemented this with good success. Was more customizable than trying to modify the select items in a custom module.

The "$form['type']['#theme'] = 'select_as_checkboxes';" goes in a custom module that you'll have to create in a form_alter hook.

I spent 1.5 days trying to change the select to checkboxes in my custom module.

I spent 1.5 hours to finish this with Ben's method.

Peter B's picture

Erik,

I just implemented this with good success. Was more customizable than trying to modify the select items in a custom module.

The "$form['type']['#theme'] = 'select_as_checkboxes';" goes in a custom module that you'll have to create in a form_alter hook.

I spent 1.5 days trying to change the select to checkboxes in my custom module.

I spent 1.5 hours to finish this with Ben's method.

Ryumkin's picture

What about "Complete Idiot's Guide"?
Can you please write more specific what and where I should write?
Pleeese...

Paolo's picture

I need a little help too!
Can you provide a step-by-step idiot-proof guide?
Thanks so much

Tevi's picture

This seems like exactly what I'm looking for, although I want to rewrite it for links, instead. But where does this code go?? I see I'm not the only one with this quesetion.

Sibee's picture

I would appreciate some further guidance on this too. So farI've registered the function in hook_theme().

I've also included "$form['type']['#theme'] = 'select_as_checkboxes';" into a module, inside a MYMODULE_form_alter() function.

You say above to: "set the field you want, in my case 'type', to use the custom theme function".

How do I find out what the field is in _my_ case - presumably it won't be 'type'? How do I find out what it is?

As Ryumkin, Paolo and Tevi above say, if anyone can produce a comprehensive step-by-step guide for this, I would very much appreciate it.

porno izle's picture

thanks for all admin
information is the most beautiful treasures

Rukaya's picture

This was very helpful, thanks. For those people who aren't sure of how to implement it, this might help:

Create a module, and then in the .module file register the theme_select_as_checkboxes function as a theme like so:

/**
* Implementation of hook_theme().
*/
function yourmodule_theme() {
return array(
'select_as_checkboxes' => array(
'function' => 'theme_select_as_checkboxes'
),
);
}

Somewhere in the .module file stick in theme_select_as_checkboxes too. Then you need to alter the correct form so that it uses the correct theme for the correct form element. It's an exposed view filter so the following function with a few modifications should work:

/**
* Implementation of hook_form_FORM_ID_alter(&$form, &$form_state).
*/
function yourmodule_form_views_exposed_form_alter(&$form, &$form_state) {
// Note: if you have problems working out what the right form #id
// is you can do print_r($form); exit(); and it will print out the
// form array for you, which will contain the #id somewhere

// Make sure that you only edit the exposed views form you want:
if($form['#id'] != 'views-exposed-form-product-grid-page-2')
return;

// Apply the theme to the form element you want!
// If you can't work out the id of the form element do the print_r
// form thing again
$form['type_tid']['#theme'] = 'select_as_checkboxes';
}

Now, just make sure your module is active and installed and away you go!

Rukaya's picture

This was very helpful, thanks. For those people who aren't sure of how to implement it, this might help:

Create a module, and then in the .module file register the theme_select_as_checkboxes function as a theme like so:

/**
 * Implementation of hook_theme().
 */
function yourmodule_theme() {
  return array(
    'select_as_checkboxes' => array( 
      'function' => 'theme_select_as_checkboxes'
    ),
  );
}

Somewhere in the .module file stick in theme_select_as_checkboxes too. Then you need to alter the correct form so that it uses the correct theme for the correct form element. It's an exposed view filter so the following function with a few modifications should work:

/**
 *  Implementation of hook_form_FORM_ID_alter(&$form, &$form_state).
 */
function yourmodule_form_views_exposed_form_alter(&$form, &$form_state) {
  // Note: if you have problems working out what the right form #id      
  // is you can do print_r($form); exit(); and it will print out the 
  // form array for you, which will contain the #id somewhere
 
  // Make sure that you only edit the exposed views form you want: 
  if($form['#id'] != 'views-exposed-form-product-grid-page-2')
    return;
 
  // Apply the theme to the form element you want!
  // If you can't work out the id of the form element do the print_r  
  // form thing again
  $form['type_tid']['#theme'] =  'select_as_checkboxes';
}

Now, just make sure your module is active and installed and away you go!

ed hardy's picture

links of london Sweetie Bracelets links of london Sweetie Bracelets
links of london Friendship Bracelets links of london Friendship Bracelets
links of london Charms links of london Charms
links of london Bracelets links of london Bracelets
links of london Necklaces links of london Necklaces
links of london Earrings links of london Earrings
links of london Rings links of london Rings
links of london Pendants links of london Pendants
links of london Silver Chain links of london Silver Chain
links of london letters charm links of london letters charm
links of london friendship links of london friendship
links of london valentine's bracelet links of london valentine's bracelet
links of london classic sweetie links of london classic sweetie
links of london raindance links of london raindance
links of london classic charms links of london classic charms
links of london heart links of london heart
links of london classic smiley links of london classic smiley
links of london animal charms links of london animal charms

Anonymous's picture

I've built a basic module around this technique and submitted it for inclusion on Drupal.org. If/When the powers-that-be approve it, I'll post a link here -- hopefully it'll be named "Better Exposed Filters". Basically, it will offer a dropdown menu in the Expose filter area that lets you specify the default display (select box) or checkbox/radio buttons (depending on the "force single" setting).

In answer to #2, you can change a single-select (ie: dropdown menu) to radio buttons by simply changing the #type in hook_form_alter(). Radios and single-select dropdowns work the same in the API. (You should also run htmlentities() on the "" option if the field is not required.) The new module will take care of that as well.

Ben, I've included a link to your site in the project credits and can include a link on the project page (assuming it's approved) if you like. Also, if you had plans to package this solution as a contributed module, please contact me at let me know as I don't want to step on any toes or steal anyone's thunder.

Thanks for posting such a simple and elegant solution!

- Mike

Gemma30's picture

I constantly buy essays or buy research papers about this post*.

Anonymous's picture

Edit to my post @17:

The parenthetical should read: You should also run htmlentities() on the <Any> option if the field is not required.

Forgot to escape my brackets...

David Eads's picture

This is a slick solution -- I'd been banging my head against the wall on this for a couple of days before I saw this. One change I made was to make an allowance for default values without a form submission. That allows me to check off some default options in my form alter hook.

  ...
  // Get the selected keys from #options if the exposed form has been submitted.
  if ($element['#post'][$element['#name']]) {
    $selected_options = (array) $element['#post'][$element['#name']];
  }
  // Otherwise, load in the default value.
  else {
    $selected_options = $element['#default_value'];
  }
  ...

Ed Hardy 	's picture

Exquisite design, superior material, exquisite craft. Ed Hardy Close to her heart, the most perfect expression of your love.

Ed Hardy 	's picture

Exquisite design, superior material, exquisite craft. Ed Hardy Close to her heart, the most perfect expression of your love.

UGG BOOTS's picture

PSST! Stunning Ugg boots

UGG BOOTS's picture

PSST! Stunning Ugg boots

bec's picture

Thanks for this post! I've been trying to transform select fields in Views exposed filters into checkboxes in hook_form_alter(), which seems to work fine until I start doing OTHER things to the form. I ended up coming back to this simple theme function :)

I use Drupal's form_clean_id() function to generate a unique id. I also determine selected values differently--because the form element is fully processed when we get it in the theme function, the selected values are already present in $element['#value'].

/**
 * Transform multiple-select fields into checkboxes in the theme layer.
 */
function theme_net2voting_views_select($element) {
  $output = '';
 
  // add standard Drupal checkbox class
  $attributes = $element['#attributes'];
  $attributes['class'] .= ' form-checkbox';
 
  foreach($element['#options'] as $key => $value) {
    // generate an id
    $id = form_clean_id($element['#id']);
 
    // note #name brackets, like in <select><options>
    $checkbox = '<input type="checkbox" '
      . 'name="' . $element['#name'] . '[]' . '" '
      . 'id="' . $id . '" '
      . 'value="' . $key . '" '
      . (in_array($key, $element['#value']) ? ' checked="checked" ' : ' ')
      . drupal_attributes($attributes) . ' />';
 
    $output .= $prefix . '<label class="option" for="' . $id . '">' . $checkbox . ' ' . check_plain($value) . '</label>' . $suffix . "\n";
  }
 
  // use Drupal to finish it off
  return theme_form_element($element, $output);
}

Adult Dating UK's picture

I just want to emphasize the good work on this blog, has excellent views and a clear vision of what you are looking for. uk date

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockcode>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]". PHP source code can also be enclosed in <?php ... ?> or <% ... %>.

More information about formatting options

CAPTCHA
Are you a robot? We usually like robots, but not in our comments.
Image CAPTCHA
Enter the characters (without spaces) shown in the image.