Disabling a checkbox with FAPI without the javascript

in drupal, fapi, forms

Forms API is great, let me start there. However there are some cases where you want to ... ahem, go around it. One such cases is with checkboxes (where you have a number of checkboxes), specifically when you want to alter just one of the set. Here's how to fix that!

The posts I've found around the net talk about changing the array by setting elements that will appear once the form is processed. Well that just didn't work for me, so I had to take the long route -- which always works.

This method is based around the #pre_render method of elements. That is, each form element can assign callbacks before it's actually converted to flat HTML. Which is great for our purpose! Here's an example form, with checkboxes, and conditional #pre_render assignement:

function mymodule_form( ) {
  [...]
  $countries = mymodule_getcountries();
	  $form['group-'.$continent][$continent] = array(
	    '#type' => 'checkboxes',
	    '#title' => t($continent),
	    '#options' => $countries,
	    '#multicolumn' => array('width' => 2),
	    '#checkall'    => true,
	    '#pre_render'  => array('mymodule_checkelement'),
	  );  
  [...]
}

function mymodule_checkelement($element) {
  [...]
  $not_allowed_countries = mymodule_getcountries();
  //Note that options are in the element array
  foreach($element as $key=>&$properties) {
    if ( in_array($key,$not_allowed_countries)) {
	$properties['#attributes']['disabled'] = true;
    }
  }

  return $element;
}

What the above code does -- it has a form builder function (which I assume you know all about), and defined the #pre_render callback. The function is then called and can perform processing to each individual option. In the above example, I've added some logic to disable checkboxes for countries that are not allowed to be checked.

NOTE:Make sure you return the $element item back, otherwise you'll get a nasty white page.
Also note that there are two non-standard elements in that form item. Once is defined by the checkall module, which allows for wonderful "Check all", "Check none", "Toggle" links on top of your checkboxes, and the other from multi-column checkbox/radios which allows multiple columns on checkboxes to be made extremely easy.

I hope this helps someone else out, it took me almost half a day to make this manageable!

Image from here