1

I have a form built with the Form API that has multiple button form elements. I don't want any of these to submit the form normally, I want them to utilize #ajax and callbacks to update the display. I feel like I run into this on every so often and can't quite remember how to make it work.

When the form first loads, I want half of the buttons to be disabled. When one of the buttons is clicked, I want to be able to detect this in buildForm so I can set form defaults and remove the disable property so other buttons can now be used.

I thought I could set storage in the callback and read it back the next time the form is built:

// Relevant bit of the form...
$form['#prefix'] = '<div id="my-form-wrapper">';
$form['#suffix'] = '</div>';

$form['actions']['#type'] = 'actions';

$form['actions']['update'] = [
  '#type' => 'button',
  '#value' => $this->t('Start Form'),
  '#ajax' => [
    'wrapper' => 'my-form-wrapper',
    'callback' => [$this, 'update'],
    'method' => 'replace',
  ]
];

$form['actions']['reset'] = [
  '#type' => 'button',
  '#value' => $this->t('Reset Form'),
  '#ajax' => [
    'wrapper' => 'my-form-wrapper',
    'callback' => [$this, 'reset'],
    'method' => 'replace',
  ],
  '#disabled' => TRUE,
];

// The AJAX callback
public function update(array &$form, FormStateInterface $form_state) {
  $form_state->set('flag_1', TRUE);
  $form_state->set('flag_2', "Started!");
  $form_state->setRebuild();
}

The callback fires, and I see $form_state storage get updated with those arbitrary values, but when the form is rebuilt, I do not see them in $form_state anymore.

I looked at a few other similar form API AJAX questions, but did not have any luck getting unstuck.

Note: I do not want to have to juggle this with jQuery. I want to be able to update form data behind the scenes and rebuild the form appropriately with the relevant data.

6
  • with #ajax you can also write #submit in form['actions']['update'] and then set the values over there. That should work. You don't want to do like that, is it ?? Commented Oct 5, 2018 at 6:18
  • Say what? I don’t want to submit and reload the page, but I want to update the form state data with the press of any button. The data isn’t in the form the next time it is rebuilt
    – Kevin
    Commented Oct 5, 2018 at 6:22
  • okay then do one thing....... $form['actions']['update'] = [ '#type' => 'button', '#value' => $this->t('Start Form'), '#submit' => ['::setAjaxSubmit'], '#ajax' => [ 'wrapper' => 'my-form-wrapper', 'callback' => [$this, 'update'], ] ]; In update just return the form field and in setAjaxSubmit method set the variable and rebuild the form Commented Oct 5, 2018 at 6:27
  • 2
    @ShreyaShetty, can you put the solution in an answer? This seems to be the the correct approach, but it's hard to read. Basically you build the form like a normal form and then ajaxify it by returning $form or the part inside of the ajax wrapper from the ajax callback to avoid a page reload. You can't change $form or $form_state from here and if the form logic works without ajax you don't need to, which is also advisable as fallback if javascript is not available.
    – 4uk4
    Commented Oct 5, 2018 at 9:36
  • @4k4 thank you for the wonderful explanation, will just put the solution in an answer Commented Oct 5, 2018 at 10:32

1 Answer 1

2

Please try this, as @4k4 suggested you have to first build a normal form and ajaxify by using #ajax, and from the method defined inside #ajax you can return a $form and any fields inside the wrapper.

// Relevant bit of the form...
$form['#prefix'] = '<div id="my-form-wrapper">';
$form['#suffix'] = '</div>';

$form['actions']['#type'] = 'actions';

$form['actions']['update'] = [
  '#type' => 'button',
  '#value' => $this->t('Start Form'),
  '#submit' => ['::setAjaxSubmit'],
  '#ajax' => [
    'wrapper' => 'my-form-wrapper',
    'callback' => [$this, 'update'],
    'method' => 'replace',
  ]
];

$form['actions']['reset'] = [
  '#type' => 'button',
  '#value' => $this->t('Reset Form'),
  '#ajax' => [
    'wrapper' => 'my-form-wrapper',
    'callback' => [$this, 'reset'],
    'method' => 'replace',
  ],
  '#disabled' => TRUE,
];

// The AJAX callback
public function update($form, FormStateInterface $form_state) {
//return $form or part inside the ajax wrapper
return $form;
}

// The submit callback
public function setAjaxSubmit($form, FormStateInterface $form_state) {
  $form_state->set('flag_1', TRUE);
  $form_state->set('flag_2', "Started!");
  $form_state->setRebuild();
}
2
  • In this example, only one of those callbacks is ever called. Xdebug never triggers in the other callback.
    – Kevin
    Commented Oct 5, 2018 at 14:00
  • You can try '#executes_submit_callback' => TRUE, or use #type => 'submit' where this is set as default, or move the code to form build where you check for the triggering element and only leave setRebuild() in the submit callback for a non ajax form submit.
    – 4uk4
    Commented Oct 5, 2018 at 18:22

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.