4

When a user pushes a button element marked with ajax, I would like the callback to modify the button value (text shown on button), and have it updated both in the browser and the form state. Updating in the browser is easy by return an ajax_command_replace call. But I'm not having any luck with getting the form updated to remember the updated button value the next time the button gets pushed.

It seems the way the ajax form is built, that the initial value of the button gets hardcoded where the callback function in the javascript page gets defined, which probably explains why the button element returned to the ajax callback does not match with the value it shows in the browser (after I changed it with the ajax_command_replace).

But I can live with that, as long as I can get the form (or form_state) to actually remember the new value of the button element from a previous invocation of the callback.

I've also tried modifying the form/form_state values in the form validate hook, but without success.

So, despite already knowing this does not work, the following code tries to illustrate what I'm trying to do:

function testajax_callback($form, &$form_state) {
  $form['mybtn']['#value'] = 'New value';
  return array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_replace('#mybtnwrapper' => drupal_render($form['mybtn']))
  );
}

Any help on how to do this the Drupal 7 way would be appreciated.

update 1:

Got a hint on the Drupal Forums. The following code is probably more according to Drupal 7 design and style:


function betapp_testajax_callback($form, &$form_state) {
  $form['mybtn']['#value'] = 'New value';
  return $form['mybtn'];
}

function betapp_testajax_form($form, &$form_state) {
  $form['mybtn'] = array(
    '#type' => 'button',
    '#id' => 'mybtn',
    '#prefix' => '',
    '#suffix' => '',
    '#default_value' => 'Default value',
    '#ajax' => array(
      'callback' => 'betapp_testajax_callback',
      'wrapper' => 'testajaxwrapper',
      'method' => 'replace',
      'effect' => 'fade'
    )
  );
  return $form;
}

The code manages to change to appearance of the button (button value), but when pressing the button with the updated value, the old one is still the one sent, so effectively, no values in the form has changed, only the button appearance.

It may have something to do with hardcoded values in the callback definitions in the html page (which is only loaded once). I find entries such as these:


{"callback":"betapp_testajax_callback","wrapper":"testajaxwrapper","method":"replaceWith","effect":"fade","event":"mousedown","keypress":true,"url":"\/d7\/system\/ajax","submit":{"_triggering_element_name":"op","_triggering_element_value":"Default value"}}

If _triggering_element_value is used regardless of the actual value of the element, that explains why the new value is not picked up. Maybe the _triggering_element_value should be looked up at runtime when the callback gets activated?

Any ideas on workarounds/fixes?

3 Answers 3

0

The short solution to my original question can be coded like this:


function betapp_testajax_callback($form, &$form_state) {
  return $form['mybtn'];
}

function betapp_testajax_form($form, &$form_state) {
  dd('');
  dd('betapp_testajax_form');
  $form['mybtn'] = array(
    '#type' => 'button',
    '#id' => 'mybtn',
    '#default_value' => 'Ignored default value',
    '#prefix' => '',
    '#suffix' => '',
    '#ajax' => array(
      'callback' => 'betapp_testajax_callback',
      'wrapper' => 'testajaxwrapper',
      'method' => 'replace',
      'effect' => 'fade'
    )
  );
  $prevval = '(blank)';
  if (isset($form_state['values']['mybtn'])) {
    $prevval = $form_state['values']['mybtn'];
    dd('prevval : ' . $prevval);
  }

  $form['mybtn']['#value'] = 'Newval ' . rand(0, 100);

  dd('newval  : ' . $form['mybtn']['#value']);
  return $form;
}

which lead to output like:


betapp_testajax_form
newval  : Newval 48

betapp_testajax_form
prevval : Newval 48
newval  : Newval 62

betapp_testajax_form
prevval : Newval 62
newval  : Newval 0

betapp_testajax_form
prevval : Newval 0
newval  : Newval 59

My usage requires a little bit more; whenever a button is changed, there are other elements that should be updated as well. But I think I managed to pull it together (looks correct for now anway), and in the spirit of sharing, here's what I managed to glue together:


function betapp_testajax_callback2($form, &$form_state) {
  return array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_replace('#mybtn', drupal_render($form['mybtn'])),
      ajax_command_replace('#inp', drupal_render($form['inp']))
    )
  );
}

function betapp_testajax_form2($form, &$form_state) {
  dd('');
  dd('betapp_testajax_form');
  $form['mybtn'] = array(
    '#type' => 'button',
    '#id' => 'mybtn',
    '#default_value' => 'Ignored default value',
    '#prefix' => '',
    '#suffix' => '',
    '#ajax' => array(
      'callback' => 'betapp_testajax_callback',
      'wrapper' => 'testajaxwrapper',
      'method' => 'replace',
      'effect' => 'fade'
    )
  );
  $prevval = '(blank)';
  if (isset($form_state['values']['mybtn'])) {
    $prevval = $form_state['values']['mybtn'];
    dd('prevval : ' . $prevval);
  }

  $form['mybtn']['#value'] = 'Newval ' . rand(0, 100);

  $form['inp'] = array(
    '#type' => 'textfield',
    '#value' => $prevval,
    '#id' => 'inp'
  );
  dd('newval  : ' . $form['mybtn']['#value']);
  return $form;
}

This code updates the original button, and sets the input field to the previous value of the button. My case requires modifying other buttons, but hopefully that will work. If not, I'll bug you on this thread again.

Thank you.

0

One more thing. If you have forms with many buttons and you use ajax like I have shown above, make sure you set the button names to unique values as well (I set mine to the same as the id property), or Drupal will have trouble recognizing which button was actually click when you modify the button values. By default, Drupal names all buttons "op" which seems to cause some trouble if you try to modify the values of multiple buttons when one of them is pressed.

0

And just to complete the information further, here is a bit more:

Special care with buttons Posted by kjeldahl on May 18, 2011 at 6:02pm

If you have forms with many buttons and you use ajax like I have shown above, make sure you set the button names to unique values as well (I set mine to the same as the id property), or Drupal will have trouble recognizing which button was actually click when you modify the button values. By default, Drupal names all buttons "op"..

Another gotcha related to $form_state['triggering_element'] Posted by kjeldahl on May 18, 2011 at 8:47pm

In my application (a simple game), I have a series of buttons that triggers an ajax callback to my Drupal module. The button's id and name is different from the value, since the value of the button is used to show some human readable information to the user. Similar to the code I've shown here, if I create a series of buttons where pushing it toggles the value (e.g. by toggling the prefix of the button value with a star or similar), it works fine as long as I keep pushing one button. However, as soon as I press another button, Drupal loses track of what was pushed, and triggers some other default action that sets the triggering_element to the first button defined by default. I've only looked briefly at form.inc and related code, and it seems the behaviour of selecting the first button is "by design".

What is possibly not by design however is the fact that Drupal is not able to track the triggering_element in the use case I described below. Dumping the state of $form_value however, led me to attempt to use something I believe is a "private" state value instead, namely $form_state['input']['_triggering_element_name'] which, differently from $form_state['triggering_element'], always seems to be populated with the correct value in my ajax application.

I believe this is a bug, but haven't got the time to track down the exact details right now. Inside form.inc there is code that seems to behave differently depending on whether the button id equals it's name and/or value, but I have no idea what the design goal for this behaviour really is. But without using _triggering_element_name it seems Drupal doesn't work well for my ajax'y button elements.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.