7

I have a form with a button that when pressed re-builds a different part of the form via an ajax request.

Upon clicking the button the form is rebuilt, then my ajax callback is called which saves the values from the form, and then returns part of the form.

The problem is the form is rebuilt BEFORE my callback which saves the values, and therefor alters how the form is built.

If I click the ajax button a second time then it works correctly as has the values from the previous ajax request when the form is build.

How can I get my callback function to run before the form is rebuilt?

function emtr_shift_plan_form($form, &$form_state) {

    ddl('build'); // log that this function is being called

    $form['save'] = array(
        '#type' => 'button',
        '#submit' => array('emtr_shift_plan_form_ajax_submit'),
        '#value' => 'Save',
        '#ajax' => array(
            'callback' => 'emtr_shift_plan_form_ajax_submit',
            'wrapper' => 'postions',
        ),
        '#id' => 'ajax-save-form-values',
    );

    $form['postions'] = array(
        '#type' => ' container',
        '#prefix' => '<div id="postions">',
        '#suffix' => '</div>',
    );

    // code here to populate the positions container

}

function emtr_shift_plan_form_ajax_submit($form, &$form_state) {

    ddl('ajax submit'); // log the ajax callback is being called

    $form_state['rebuild'] = TRUE; // As the form has already been rebuilt at this point what effect can this have?!?

    emtr_shift_plan_form_save($form, $form_state); // save the values

    // This is the point at which I want the form to be rebuilt

    return $form['positions'];

}

EDIT:

As a workaround I found it is possible to manually rebuild the form a second time in the ajax callback with $form = drupal_rebuild_form('emtr_shift_plan_form', $form_state);, but is seems inefficient building the form twice. Can I just swap the order of these events over as opposed to having the build the form twice per ajax request?

1
  • 2
    You should be doing the 'saving' in a submit function. Ajax callbacks should generally just return the form element. It is possible that this will also fix your problem.
    – 2pha
    Commented Dec 10, 2014 at 4:51

2 Answers 2

2

I know this question is 3 years old, but I came upon it while searching for a similar issue.

You'll need to modify your approach to get this working. The order of operations when you submit the AJAX request is this:

  1. AJAX submit callback is fired. In your AJAX submit callback you'll want to save the data. Flag it in your $form_state variable.
  2. Form is rebuilt.
  3. AJAX callback is fired.

I've re-worked your code a little bit to demonstrate.

// AJAX FLOW: 2. Form rebuild is fired.
function emtr_shift_plan_form($form, &$form_state) {

    ddl('build'); // log that this function is being called

    $form['save'] = array(
        '#type' => 'button',
        '#submit' => array('emtr_shift_plan_form_button_submit'),
        '#value' => 'Save',
        '#ajax' => array(
            'callback' => 'emtr_shift_plan_form_ajax',
            'wrapper' => 'postions',
        ),
        '#id' => 'ajax-save-form-values',
    );

    $form['postions'] = array(
        '#type' => ' container',
        '#prefix' => '<div id="postions">',
        '#suffix' => '</div>',
    );

    // code here to populate the positions container
    if (isset($form_state['saved_values'])) {
      // do stuff.
    }
}

// AJAX FLOW: 1. Button submit is fired.
function emtr_shift_plan_form_button_submit($form, &$form_state) {

  // log the ajax callback is being called
  ddl('ajax submit');

  // save the values. Make sure the data is stored somehow so your form can get at it.
  $form_state['saved_values'] = emtr_shift_plan_form_save($form, $form_state);

  // rebuild flag here will trigger emtr_shift_plan_form() to be run
  $form_state['rebuild'] = TRUE;
}

// AJAX FLOW: 3. Button callback is fired.
function emtr_shift_plan_form_ajax($form, &$form_state) {
    return $form['positions'];
}

Hope that sheds some light for someone.

2

You could try like this making submit different and callback different and see if it helps.

$form['save'] = array(
    '#type' => 'button',
    '#submit' => array('emtr_shift_plan_form_ajax_submit'),
    '#value' => 'Save',
    '#ajax' => array(
        'callback' => 'emtr_shift_plan_form_ajax',
        'wrapper' => 'postions',
    ),
    '#id' => 'ajax-save-form-values',
);

function emtr_shift_plan_form_ajax($form, &$form_state){
  return $form['positions'];
}
function emtr_shift_plan_form_ajax_submit($form, &$form_state) {

    ddl('ajax submit'); // log the ajax callback is being called

    $form_state['rebuild'] = TRUE; // As the form has already been rebuilt at this point what effect can this have?!?

    emtr_shift_plan_form_save($form, $form_state); // save the values

    // This is the point at which I want the form to be rebuilt
}
2
  • Thanks for your suggestion but doesn't seem to make any difference. That button shouldn't really need a submit handler at all if it has an ajax callback defined.
    – Felix Eve
    Commented Mar 24, 2014 at 16:37
  • This is indeed the right answer along with 2pha comment: ajax callback functions just return the output. Any other actions should be taken in a submit function (if element is a button or equivalent) or in the functions that return the form (or any alter functions). So, the question is not how to rebuild after callback, is how to do any calculations before the ajax callback function. Tew wrong assumption here is to think that the ajax callback function is a submit function: is just a function that returns the output: a form fragment or AJAX commands.
    – sanzante
    Commented Mar 16, 2015 at 19:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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