I want to dynamically change the default selected value of an optionwidgets_select inside hook_form_alter(), is that possible?

When my form_alter() is called, I don't see the actual select value list, so I can't change it. The cck field has a type of optionwidgets_select.

Comments

newbuntu’s picture

I used this kludge. Inside form_alter() I added the following
$form['field_country']['#default_value'][0]['value']='USA';

It sort of works, but I really want to know the right way to do it.

arcane’s picture

I would like the same functionality to stuff the value of my nodereference, and then limit the options to only the stuffed value.

arcane’s picture

I used the form #post_render property to modify the values of a nodereference options widget (as described in the book Pro Drupal Development 2nd Edition page 254)

In my hook form alter I have:

$form['field_requestor_event_reference']['#post_render'] = array(set_content_noderef); // Call this post render function set_content_noderef

and then I have this element handler:

function set_content_noderef($content, $element) {

        $mynid = $element['#default_value'][0]['nid'];
        $event_desc = db_result(db_query('SELECT title from {node} where nid = %d and type="press_event"', $mynid));
        $new_content = '<div class="form-item" id="edit-field-requestor-event-reference-nid-nid-wrapper">' .
                       '<label for="edit-field-requestor-event-reference-nid-nid">Event: </label>' .
                       '<select name="field_requestor_event_reference[nid][nid]" class="form-select" id="edit-field-requestor-event-reference-nid-nid">' .
                       '<option value="' . $mynid . '" selected="selected">' . $event_desc .  '</option></select></div>'; 
        return $new_content;

}

This is working for me most of the time. Occasionally I get the error "An illegal choice has been detected. Please contact the site administrator." when I try to submit the form. According to this article, http://groups.drupal.org/node/1839, it means that the that the options passed into the form didn't match what the form gave it, and would inform you that an illegal choice had been made. Unfortunately, the #DANGEROUS_SKIP_CHECK property has been removed in Drupal 6.

I am not sure why this works some percentage of the time, and why at other times, Drupal 6 seems to complain about it. I have not found a workaround for this yet. Can someone please advise? Do I need to change the value of $element to match what is in $content? (can this be done if I pass by reference?)

arcane’s picture

I found out why I was getting the error "An illegal choice has been detected. Please contact the site administrator." This was caused by me trying to assign a value to the nodereference that was not included in the option values returned by the view (in the noderef configuration, advanced section, view used to select the nodes). I forgot I had set this to a view and removed this setting entirely. Now the above code is working for me 100% of the time.

arcane’s picture

I also found that I could do this in a cleaner fashion using the elements #pre_render property.

In my hook form alter I have:

$form['field_requestor_event_reference']['#pre_render'] = array(pre_ren);

and then I have this element handler: (I deduced I could have an element handler by looking at the patch at http://drupal.org/node/147662)

function pre_ren($element) {
        $mynid = $element['#default_value'][0]['nid'];  // I had previously stuffed the desired default value in the hook form alter, look at #1 above
        $event_title = db_result(db_query('SELECT title from {node} where nid = %d and type="press_event"', $mynid));

        // Limit the options of the widget to the values in the array that I set
        $element['nid']['nid']['#options'] = array($mynid => $event_title);
        return $element;
}

I really hope this helps somebody, because I have spent 3 days working this out.

beautifulmind’s picture

I tried this and also post_render.
My widget is filled up using javascript, by firing change event of another drop down box.
But no help.

Can you shed some lights on this?

Regards.

jhedstrom’s picture

Thanks arcane, that helped a lot. I was stumped about altering options (in my case, I just wanted to wrap each one in CSS classes), and the #pre_render function worked perfectly.

pcambra’s picture

Thanks arcane, the #pre_render attribute has fit just what I needed

Harry Slaughter’s picture

so to boil it down, you're doing something like this:

In your hook_form_alter, you do something like this:

<?php
        $form['somefield']['#pre_render'] = array('some_function');
?>

then you have a function like this:

<?php
 function some_function($element) {
  // do some stuff here to determine desired options, then:

  $element['value']['#options'] = $options;

  return $form;
}

?>

note that i assign the custom options to $element['value']['#options'] because that's where cck's optionswidgets_select stores the actual options for some mysterious reason.

that will render a form with the desired options, but it also still generates the 'An illegal choice...' error when you submit the form.

this is a real killer. you can't do a traditional hook_form_alter on a optionswidgets_select field because CCK has not rendered the field completely yet. but you can't alter the form after the fact using pre_render because the validation has run and you end up 'corrupting' the form (and getting the error).

i'd really like to see some functional sample code of optionswidgets_select options being set dynamically.

oh, and you can't use a php function in the optionswidgets_select configuration because it gets called before your node is loaded, so you have no info to generate custom options!

Harry Slaughter’s picture

I figured out that the options for an optionwidgets_select element are accessible from hook_form_alter(), but not in
the place one would expect ($form['my_cck_select_field'][#options]) but instead are stored as a string in $form['#field_info']['my_cck_select_field']['allowed_values']. I can set a string value of options there and it will show up in the rendered form.

Icky, but it works.

yurtboy’s picture

wow.
Still searching but alteast it there is hope.
I did notice #9 has return $form but I think it is suppose to be return $element

I am trying to add ahah
Any thoughts?

function alter_state($element) {

$element[0]['#ahah'] = 
    array(
      'event' => 'change',
      'path' => 'lhgarden/message_js',
      'arguments' => 'state',
      'wrapper' => 'gpfeedback',
      'effect' => 'fade',
    );
$element['value']['#title'] = 'Planting State...';
return $element;
}

I tried [0] and [value] before the ahah

Gary Feldman’s picture

What sort of changes using #pre_render will cause validation to fail? In my case, I just want to add an image to each radio button. If I simply insert the <img> tag after the <input> tag, will that work?

Also, the #field_info section isn't there if you use hook_FIELD_ID_form_alter, because as has been noted, it hasn't been built yet. There is a stub entry under the #parameters section, but it just has the field name with an empty array value. I'm halfway tempted to try an explicit call to content_form_alter directly from my hook_FIELD_ID_form_alter, in order to populate the form. But that seems like too gross of a hack.

The other option I may look into is using the #process attribute. My hunch, though, is that #pre_render ought to work for me, even if doesn't work for Harry, since I'm not changing any values, just adding markup.

WildKitten’s picture

@Harry

Please could you post some code here. I am trying to do something like this for a week, and I'm out of my mind.

markus_petrux’s picture

Status: Active » Fixed

I would say playing with the form and FAPI capabilities is a generic issue in Drupal. Please, consult FAPI documentation and/or use the development forums for further assistance.

Another source for help is the CCK handbook pages. This can be extended with more examples and code snippets, so that the recipes that one can come up with can be of help to others. Please, use those resources. Thanks

krem’s picture

Hi guys,

I have also been scrashing my head about this for a couple of days, and I finally got something working on my case but as a work around, so if somebody find THE proper way to do it, please post it !!

I have a node reference field in a select box:

My working solution -> In the configuration window of the field, as a default value, I put

// If we are in the creation mode for my_node_type - This condition is needed in my case to go through the validation process, else arg(3) is not recognized
if(arg(0) == 'node' && arg(1) == 'add' && arg(2) == 'my_node_type'){

//This is the returned value, arg(3) correspond to the node id, so replace it by your own variable
return array(
  0 => array('nid' => arg(3)),  
);
}
else{
return array(
  0 => array('nid' => 1),  
);
}

Searching around (http://drupal.org/node/283640) I think I found another way to select the desired option through form_alter:

function xxx_form_alter(&$form, $form_state, $form_id) {
    switch($form_id)
    {		
	case 'my_type_node_form':		
		$form['#attributes'] = array( 'onChange' => "top.location.href=document.getElementById('$formname').options[document.getElementById('$formname').selectedIndex].value");			
	break;			
    }
}

With this code, I am able to add an attribute to the form, obviously the code need to be updated for our case, but replacing onChange with onLoad and using a simple javascript to update the selected value should not be that hard. So sorry, but I got to run after the clock, I hope this will unblock some of you.

IMPORTANT NOTE: DO NOT ONLY REFRESH YOUR FORM PAGE TO TEST, IN MY CASE I CHANGE AN ARGUMENT IN THE URL, IF NOT, EVEN IF THE CODE CHANGE IS VALID, IT WON'T WORK EVEN AFTER CLEANING THE CACHE THROUGH PERFORMANCE TAB IN THE ADMIN SECTION...( Don't ask me why ;) )

Bye guys
Clem

jhedstrom’s picture

@markus_petrux since node referrence doesn't make the select options available until the rendering stage, this thread isn't a typical hook_form_alter() support request since it requires a bit of trickery as seen in comments above.

markus_petrux’s picture

Yes, and that's a generic Drupal development issue. Since it's tricky, it would be nice if someone can come up with a snippet for the CCK handbooks. That way it would potentially help a lot more people than a never ending support request in the issue queue. The handbooks can be added and/or edited by anyone. See this chapter for example:

http://drupal.org/node/101742

If the handbooks were edited by more people, than we would have less support requests like this one.

Installing a pre_render callback is not so complex. See other examples in the handbook on how to use after_build, which is another possible method to resolve this issue, probably. Then, if you don't know the format of any element in the form, you can just use any var_dump variant to figure it out, and then you're almost done. Writing about this will potentially help more than an issue where different use-cases are mixed.

I think keeping this fixed may encourage better resource usage here. An issue in the CCK queue is much harder to find than a handbook page.

I fI had the time, I would write about it, but I don't at this time. I already wrote one, and that may also help here, I think.

For generic drupal development issues, the best resource is the development forums. This is just noise in issues queue.

Gary Feldman’s picture

I must disagree. I find it much easier to locate information in the issues queue than in the forums.

I also don't understand why this isn't a CCK-specific issue. One of the problems seems to be that CCK, at least in some cases, doesn't build a proper form before the hook_form_alter functions are being called, and the hook_FIELD_ID_form_alter functions can't be made to work at all. If a mechanism works with traditional content types provided directly by modules, but doesn't work with CCK types, then that's a CCK issue, not a Drupal issue, isn't it?

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

toemaz’s picture

Anotehr solution how to inject select options in a CCK field:

Instead of optionwidgets_select, choose a text_textfield, i.e. a normal text field.
Implement hook_form_alter():

    // create your select options array
    $options = array();

    $form['field_my_textfield'][0]['#type'] = 'select';
    $form['field_my_textfield'][0]['#options'] = $options;

    // since the tree of a textfield comes out as field_my_textfield[0][value]
    // and the one from a select as field_my_textfield[0]
    // we need to move up the select one up in the tree adding a [value] child
    $element = $form['field_my_textfield'][0];
    unset($form['field_my_textfield'][0]);
    $form['field_my_textfield'][0]['value'] = $element;

This works perfect for me.

cwebster’s picture

EDIT: If you just use hook_form_alter() to add a new option to the $form['#field_info'][MYFIELD]['allowed_values'] string, everything seems to work just fine.

...

I'm currently working on a contrib module that needed to be able to do this. Here is an example of how I was able to use a module to add an option to the beginning of a select box (optionwidgets_select) and not receive the "An illegal choice has been detected. Please contact the site administrator." error.

/**
 * Function to be referenced as a callback in a field's #pre_render array key.
 */
function MYMODULE_add_option($element){

  // Add a "- Select One -" option to the beginning of the field's #options array.
  $element['value']['#options'] = array('- Select One -'=>'- Select One -') + $element['value']['#options'];

  return $element;
}

/**
 * Implementation of hook_form_alter.
 */
function MYMODULE_form_alter(&$form, &$form_state){

  // Add the "- Select One -" option to the $form array.
  $form[MYFIELD]['#pre_render'] = array('MYMODULE_add_option');

  // Add "- Select One -" to the allowed values for this field.
  // (This is necessary to avoid the "An illegal choice has been detected. Please contact the site administrator." error.
  $form['#field_info'][MYFIELD]['allowed_values'] = "- Select One -\n".$form['#field_info'][$key]['allowed_values'];

}

The key was to add the value to the select box using #pre_render *AND* the $form['#field_info'][MYFIELD]['allowed_values'] string, since Drupal checks against the allowed values list.

Hope that helps someone. Let me know if I'm doing anything crazy.

jmlavarenne’s picture

Most useful!

davepoon’s picture

I have successfully overridden/changed the select list that base on content type without the "An illegal choice has been detected. Please contact the site administrator." error.
Thank you, this is very informative.

texas-bronius’s picture

Simply because I came across this closed thread seeking how to set a CCK Options widget in a hook_form_alter to pre-populate values based on a copy. My eureka came when setting the value in a $form['#after_build'] function:

          if($form['group_installation_address'][$installkey]['#type'] == 'optionwidgets_select') {
            $form['group_installation_address'][$installkey]['value']['#value'] = $field[0]['value'];
          } else {
            $form['group_installation_address'][$installkey][0]['value']['#value'] = $field[0]['value'];
          }

If the destination field whose value I'm setting is a select/combo box, it is not in the 0 array element, unlike other fields!

(Note to anyone else coming across this-- the thread poses three different scenarios, four if you count the unresolved AHAH query above)

yogesh1110’s picture

Have you got any idea on ahah attached on a field in noderefference

owolawi’s picture

In addition, you may need to add:

$form['field_my_textfield'][0]['#validated']= TRUE;

I had the same error message when I was population cck option widget with jquery.

The above approach of using text message and altering to options works for me.

I only have to add

$form['field_my_textfield'][0]['#validated']= TRUE;

ytsurk’s picture

definitly a nice one:
['#validated']= TRUE;

i was going crazy ..

['#DANGEROUS_SKIP_CHECK']= TRUE;
had no effect at all (in D7.8) ..

alien’s picture

Better solution here:
http://drupal.org/node/726282

function MODULENAME_form_alter(&$form, $form_state, $form_id) {
   $form['#after_build'][] = 'MODULENAME_action_after_build';
}

function MODULENAME_after_build($form, &$form_state) {
  $form["group_name"]['field_name']['value']['#options'] += MODULENAME_add_options();

  return $form;
}
gauravktomar’s picture

Thanks @owolawi

['#validated']= TRUE is really a perfect solution.

I actually tried many solutions, but failed. Finally used #validated, and it worked. Thanks Man!!!!!!

Best!
Gmeet

oknate’s picture

Great suggestion #10, Harry Slaughter ! This gets around the illegal choice issue. You alter the options before the optionwidget_select is rendered, so it won't give you the illegal choice problem.

I wanted something I could check into SVN, so I didn't want to use the php option in the admin panel.


function microsite_form_alter(&$form, $form_state, $form_id) {

  if($form_id == 'customheader_node_form') {
    $form['#field_info']['field_header_menu']['allowed_values'] = microsite_customheader_menu_options();
  }
  
}
/*
* the options at this point, when it's in the #field_info, it's in the abstract, and hasn't been turned into a php array yet, so it wants the format of each option having the key, a pipe "|" and then value on a line
*/
function  microsite_customheader_menu_options() {
  $options = menu_get_menus();
  
  $pipes = '';
  
  foreach($options as $key => $value) {
    $pipes[] = $key . '|' . $value;
  }

  $output = implode("\n",$pipes);
  
  return $output;
}

ilrecidivo’s picture

Thanks oknate!
+1

lachyn’s picture

After trying all above, this was the only combination of code that worked for me in D7.16 for altering a select field with my computed values:

function MYMODULE_form_alter(&$form, $form_state, $form_id) {
   if ($form_id == 'NODETYPE_node_form') {
   	$form['#validated'] = TRUE;
   	$form['field_MYFIELD']['und']['#options'] = MYMODULE_add_options();
	return $form;
   }     
}

function MYMODULE_add_options() {
   $result = db_query.........;
   //prepare your $options array here
   return $options;   	
}
rakesh.gectcr’s picture

form_alter will work
I am getting nid from url
like www.yourwebsite/node/add/forum?nid=123

<?php
/**
 * Implements hook_formalter().
 */

function YourModule_form_alter(&$form, &$form_state, $form_id) {
 //dsm($form_id);
 //dsm($form);
// print "<pre>";
// print $form_id;
// print_r($form);
		if($form_id=='FORM-ID'){
			if($_GET['nid']){
				$form['YOUR-FIELD-VALUE']['und']['#default_value']=$_GET['nid'];
			}
		}
}