A new \Drupal\commerce\AjaxFormTrait has been added, providing an #ajax callback that can be used to refresh an entire form.
Handles the tricky details around inserting status messages.
From the code:
/**
* Ajax handler for refreshing an entire form.
*
* All status messages need to be displayed when refreshing the form.
* In large forms, it is a best practice to output these messages close
* to the triggering element. For example, when ajax is triggered at
* checkout, the messages should be shown above the relevant checkout pane.
* When ['#ajax']['element'] is specified, the messages will be shown above
* it. Otherwise, the status messages will be shown above the whole form.
* Example:
* $inline_form['apply']['#ajax']['element'] = $inline_form['#parents'];
*
* Note that both the form and the element need to have an #id specified,
* as a workaround to core bug #2897377. This was already done for
* checkout forms, checkout panes, and inline forms.
The trait has been added to CheckoutFlowBase and InlineFormBase, making it available by default in any part of checkout, and in any inline form. All that's needed is to use the #ajax callback. From the CouponRedemption inline form:
$inline_form['apply'] = [
'#type' => 'submit',
'#value' => t('Apply coupon'),
'#name' => 'apply_coupon',
'#limit_validation_errors' => [
$inline_form['#parents'],
],
'#submit' => [
[get_called_class(), 'applyCoupon'],
],
'#ajax' => [
'callback' => [get_called_class(), 'ajaxRefreshForm'],
// Specifying the element is optional.
'element' => $inline_form['#parents'],
],
];
Note that any other class implementing AjaxFormTrait needs to set $form['#id'] in the buildForm() method, as a workaround for core bug #2897377: $form['#attributes']['data-drupal-selector'] is set to a random value so can't be used for its intended purpose. Example from CheckoutFlowBase:
// Workaround for core bug #2897377.
$form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']);