Hi,

I have created a form submit handler that reads the &$form_state['triggering_element'] to work out which button was pressed, however it seems to be getting the id of the last button to be rendered/the one with the highest id, rather than the id of the button actually pressed...

I am confused because I thought triggering_element was the correct way to work out which button was pressed in a form where there are multiple buttons..

any help much appreciated

cheers,

Rowan

Comments

duckzland’s picture

any sample code will be a great help to find out the errors

--------------------------------------------------------------------------------------------------------
if you can use drupal why use others?
VicTheme.com

rowanpol’s picture

Hi,

Im using the following statement to write the id of the button.

$form_state['triggering_element']['#id']

and its getting an actual id, just the last one on the page rather than the one that was clicked..

rowanpol’s picture

I ended up solving this by using the #name instead of #id..

nagiek’s picture

Just set the #name when writing the button. Thanks a ton!

AlexZt’s picture

I set the diffferent "#name" for each button.
Also don't forget to set the #type = 'submit' instead of a 'button'

Thank you

mathieso’s picture

This code is added to a form several times, with various values for $animal_nid:

  $animal_form['detach_button'] = array(
    '#type' => 'button',
    '#value' => 'Detach',
    "#executes_submit_callback" => FALSE,
    '#limit_validation_errors' => array(),
    '#process' => array(),
    '#submit' => array(),
    '#name' => 'doof'.$animal_nid,
      //W/o this, Drupal things the triggering element is the last submit on the
      //page. With the line, it works. !!!!!!!!!!!!!!
    '#ajax' => array(
      'callback' => '_zoo_update_animal_section_in_browser',
      //Replace the entire animals section.
      'wrapper' => 'animals_container',
    ),
    '#attributes' => array(
      'data' => array(
        'command' => 'detach',
        'animal_nid' => $animal_nid,
      ),
    ),
  );

Clicking the button rebuilds the form, calling hook_form_alter. When #name is not set, during hook_form_alter $form_state['triggering_element'] points to the wrong submit button - the last one on the page.

When #name is set as above, triggering_element points to the right button (the one the user actually clicked).

#name doesn't have to be used in the code, just has to be present.

majestixx’s picture

The solution with setting #name unfornately does not work for me.
triggering_element has still the last button as its value.

Also specifying different callback functions for each button does not work for me.
The first button is triggering the submit function of the second one.

function taxonomy_pivot_user_form(){
    $form['submit_button'] = array(
        '#type' => 'submit',
        '#name' => 'submit_button',
        '#value' => t('Process'),
        '#ajax' => array(
            'callback' => 'taxonomy_privot_ajax_callback',
            'wrapper' => 'replace_area',
        ),
        '#executes_submit_callback' => FALSE,
    );

    $form['excel_export'] = array(
        '#type' => 'image_button',
        '#name' => 'excel_export',
        '#src' => drupal_get_path('module', "taxonomy_pivot") . '/images/excel.png',
        '#value' => t('Excel Export'),
        '#submit' => array('taxonomy_privot_excel_callback'),
    );
}

function taxonomy_privot_excel_callback($form, $form_state){
	return taxonomy_privot_ajax_callback($form, $form_state, "excel");
}
function taxonomy_privot_ajax_callback($form, $form_state, $op = "table"){
    dd($op);
}

No matter which button is clicked, dd($op) is always "excel".
Only if the second button is removed, the first one is working properly and $op is "table.

operations’s picture

Finally, I got it to work .. I was getting the "Notice: Undefined index: #ajax in ajax_form_callback()" error for ages and I googled google to find a solution to my problem but in vain!

I have the below situation:

  1. Drupal 7.20
  2. Entity reference prepopulate - to populate a dropdown entity reference field
  3. Two Entity reference fields (checkboxes) with views filtering enabled (entity reference display)
  4. Hook form alter to add ajax functionality and more

The issue: I wasn't able to bind the two entity reference fields together using #ajax. The $form_state['triggering_element']['#ajax'] array wasn't available in ajax.inc ajax_form_callback.

The reason: I was restricting access to the scope of code that adds the #ajax to the desired field.I was checking a $_GET parameter for the entity reference prepopulated field at the beginning of the form alter. This causes the ajax request (path: system/ajax) to not access the code and not rebuild the form again with #ajax property. So check that the ajax request when executed it sees the ajax code again. This is my understanding so far cause when I added the below it worked perfectly:

if (isset($form_state['input']['my_ajax_field_name']) || isset($_GET['my_get_parameter_name'])) {

Though I think the best practice is to set the path in the #ajax array to something like 'system/ajax?nid=nid' which I'm still not being able to do.. cause the ? char gets wrong. Any ideas?

jaypan’s picture

The Drupal way of having multiple versions of the same form on a page is to use hook_forms(). This allows you to use unique form IDs that all output the same form (but set the attributes on the form so that the correct form is submitted).

Contact me to contract me for D7 -> D10/11 migrations.

kitt2012’s picture

Hi Jaypan,

Just for the record, I love your work and your comments on drupal.org have helped me no end.

I'd like to ask a question based on the thread above. I have a similar issue.

I have many buttons on one form, but am currently testing with just two.
Both are ajax buttons.

Clicking the buttons seem to deliver incorrect results.

Test1. Click button one - ajax call passed.
Test2. Click button two - ajax call passed.
Test3. Click button one, then button two. - ajax calls (both) - passed.
Test4. Click button one, then button two, then button one again. - fail.

In test4, the first two clicks fire the relevant ajax callback but the third calls the first buttons call back again.

I have printed the $form_state of the form after each process. In test four $form_state['triggering_element']['#name'] = button2 for the final click. However $form_state['input']['_tirggering_element_name'] = button1.

All button are loaded into $form_state['buttons']. And all buttons are #type 'button'. All buttons have a unique #name and #value.

It seems to me, in _form_builder_handle_input_element() the function _form_button_was_clicked() sets a button name to triggering_element if it's value and a name are equal. As all the buttons have a value, then the final button will always set the triggering_element.

So I suppose the question I am asking is how do we unset a button set later in the form value to ensure a previously clicked button can be registered as the triggering_element.

Your time is greatly appreciated.

kitt2012’s picture

If button one is clicked a second time,

ie click Button1, click Button2, click Button1, click Button1.

It the ajax call for button 1 will fire for the last click. But for the third click, Button2's ajax callback will still fire.

kitt2012’s picture

Okay what I've decided to do is this...

Test for $form_state['triggering_element'] and if it exists set a variable $input_trigger to $form_state['input'][_triggering_element_name].

This variable can now fire all relevant functions that apply to that trigger.

Each of the elements now have the same ajax callback.
Inside this callback, switch $form_state['input'][_triggering_element_name] and apply the correct ajax commands to the correct trigger.

Is this method advisable?

$addform_builder($form,$form_state){

 ...

 if(array_key_exists('triggering_element',$form_state)) {
		
      $input_trigger = $form_state['input']['_triggering_element_name'];//$form_state['triggering_element']['#name'];//
		
      _addform_triggers($form, $form_state, $input_trigger);
				
 }

 /**

    Other form odds and sods

 */

 $form['buttons']['update_venue_jd'] = array(
     '#type' => 'button',
     '#name' => 'update_venue_jd',
     '#value' => t('update_venue_jd'),
     '#limit_validation_errors' => array(),
     '#prefix' => '<div id = "update_venue_jd" title="Update Venue">',
     '#suffix' => '</div>',
     '#ajax' => array(
          'event' => 'click',
          'callback' => 'fcc_addform_buttons_callback',
          'wrapper' => 'some_container',
	  'method' => 'replace',
			),	
 );
 $form['buttons']['updatediscipline'] = array(
	'#type' => 'button',
	'#name' => 'updatediscipline',
	'#value' => t('updatediscipline'),
	'#limit_validation_errors' => array(),
	'#prefix' => '<div id = "update_jd" title="Update selected job details">',
	'#suffix' => '</div>',
	'#ajax' => array(
		'event' => 'click',
		'callback' => 'fcc_addform_buttons_callback',
		'wrapper' => 'some_container',
		'method' => 'replace',
		),	
 );

 $form['operation'] = array(
       '#markup'=>'<pre>'.print_r($form_state['input'],TRUE).'</pre>'),
       '#prefix' => '<div id = "fcc_op_replace" >',
		'#suffix' => '</div>',
 );

  return $form;

}

function fcc_addform_buttons_callback(&$form, &$form_state){
	
	$input_trigger = $form_state['input']['_triggering_element_name'];
	$commands = array();
	switch($input_trigger){
		case 'quantitybutton':
		break;
		case 'refresh';
		break;
		case 'clients':
		break;
		case 'updatediscipline':
			$commands[] = ajax_command_alert(t('ajax: updatediscipline is working'));
			$commands[] = ajax_command_replace("#some_container", render($form['some_container']));
			$commands[] = ajax_command_replace("#fcc_op_replace", render($form['operation']));	
		break;
		case 'chk_reset':
		break;
		case 'update_venue_jd':
			$commands[] = ajax_command_alert(t('ajax: update_venue_jd is working'));
			$commands[] = ajax_command_replace("#details_container", render($form['details_container']));
			$commands[] = ajax_command_replace("#fcc_op_replace", render($form['operation']));		
		break;
		case 'startdate':
		break;
	
	}
	
	return array(
		'#type' => 'ajax',
		'#commands' => $commands,	
	);
	die();
}

Anyway....

A little further reading of _form_builder_handle_input_element(). It seems that if the $form_state['triggering_element'] is set at _form_element_triggered_scripted_submission, it should skip the following _form_button_was_clicked(). But it doesn't.

Have I missed something vital?

Any advise would be great, thanks again.